97 Commits

Author SHA1 Message Date
666129de16 New release 2021-06-02 22:29:07 +02:00
becdbd6546 Hunting crash in Fdroid version 2021-06-02 17:12:33 +02:00
d292988737 Hunting crash in Fdroid version 2021-06-01 20:07:15 +02:00
21ee06e9b1 Wifi tethering without root above Oreo works again. 2021-05-30 20:03:30 +02:00
f22e4854ee Merge branch 'WifiRouter' into development
# Conflicts:
#	app/src/main/res/values-es/strings.xml
2021-05-30 12:28:59 +02:00
7182698b8a Wifi tethering without root above Oreo works again. 2021-05-30 12:27:27 +02:00
7fd8d1cfd0 Translation 2021-05-26 19:55:21 +02:00
943928089b Translation 2021-05-22 23:12:40 +02:00
f70e45701f Wifirouter 2021-05-22 02:51:31 +02:00
9b33f13f66 Italian translation updated 2021-05-21 19:52:36 +02:00
2f3a33b1b8 Translation 2021-05-20 23:11:16 +02:00
6c8ca59e3f Spanish translation 2021-05-19 21:06:15 +02:00
5ffb36a87f Added read_phone_state for action setDataConnection 2021-05-18 22:30:01 +02:00
1560fd3343 SuperSu related changes. 2021-05-18 20:02:45 +02:00
a0ff8c80f0 Spanish translation 2021-05-16 22:51:03 +02:00
1ea3bdaea3 Permissions constants used. 2021-05-16 19:57:05 +02:00
f325b30917 Permissions constants used. 2021-05-16 19:51:22 +02:00
8ce2a09b3b Wifi trigger mgmt change 2021-05-16 14:27:54 +02:00
4a18a6ed19 Wifi trigger mgmt change 2021-05-16 02:32:31 +02:00
9a8519d3e3 Phone Listener changed. 2021-05-15 19:55:48 +02:00
0a0399c2b0 Phone Listener changed. 2021-05-15 14:22:43 +02:00
3844079781 Phone Listener changed. 2021-05-15 02:27:59 +02:00
191ad904a3 Phone Listener changed. 2021-05-14 20:07:46 +02:00
e988cedf7c Phone Listener changed. 2021-05-14 13:00:25 +02:00
9a7f66fa22 PhoneCallTrigger 2021-05-13 23:50:14 +02:00
c926c85dd0 Phone Listener changed. 2021-05-13 18:37:34 +02:00
cddd8cfac5 Build 2021-05-13 14:44:29 +02:00
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
bdfded960c Migration of old files unfinished. 2021-03-12 22:38:52 +01:00
d803d57af1 Changed settings location to regular data folder. Migration of files in existing installations pending. 2021-03-07 23:45:47 +01:00
f9cf3356e7 Merge branches 'development' and 'master' of https://git.server47.de/jens/Automation into development 2021-03-07 12:08:29 +01:00
81be087252 Google shit again. 2021-03-06 02:04:01 +01:00
99d630baae Merge branches 'development' and 'master' of https://git.server47.de/jens/Automation into development 2021-03-06 00:25:53 +01:00
d64f6e2c3b News download finished. 2021-03-02 19:55:43 +01:00
8a5fa20cad Started SDK 29 background location crap. 2021-02-28 23:06:20 +01:00
5758e5ee2b News done. 2021-02-25 22:50:36 +01:00
129 changed files with 11429 additions and 4215 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 96
versionName "1.6.21"
versionCode 105
versionName "1.6.34"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -70,6 +70,9 @@ dependencies {
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0'
implementation 'com.linkedin.dexmaker:dexmaker:2.25.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
testImplementation 'junit:junit:4.+'

View File

@ -0,0 +1,18 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.jens.automation2",
"variantName": "googlePlayFlavorRelease",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 104,
"versionName": "1.6.33-googlePlay",
"outputFile": "app-googlePlayFlavor-release.apk"
}
]
}

View File

@ -51,6 +51,7 @@
<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.WRITE_SECURE_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" />
@ -62,8 +63,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 +77,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 +96,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 +137,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=".ActivityManageTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
@ -177,13 +182,34 @@
<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=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<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>
<activity android:name=".ActivityPermissions" />
<!-- 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=".receivers.ActivityDetectionReceiver"
@ -193,15 +219,15 @@
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<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"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>
</manifest>

View File

@ -1,13 +1,18 @@
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.telephony.TelephonyManager;
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 +21,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 +30,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 +52,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 +135,7 @@ public class Rule implements Comparable<Rule>
{
return this.getName();
}
@SuppressLint("NewApi")
public String toStringLong()
{
String returnString = "";
@ -199,7 +221,7 @@ public class Rule implements Comparable<Rule>
private boolean checkBeforeSaving(Context context, boolean changeExistingRule)
{
if(this.getName() == null | this.getName().length()==0)
if(this.getName() == null || this.getName().length()==0)
{
Toast.makeText(context, context.getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false;
@ -394,13 +416,13 @@ public class Rule implements Comparable<Rule>
&&
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0
)
|
||
// Other case, start time higher than end time, timeframe goes over midnight
(
Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0
&&
(Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0
|
||
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0)
)
@ -469,7 +491,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 +499,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 +510,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 +518,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;
@ -527,12 +549,12 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for wifi state", 4);
if(oneTrigger.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected
{
if(oneTrigger.getWifiName().length() > 0) // only check if any wifi name specified, otherwise any wifi will do
if(oneTrigger.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name specified, checking that.", 4);
if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getWifiName()))
if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getTriggerParameter2()))
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getWifiName(), WifiBroadcastReceiver.getLastWifiSsid()), 3);
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), 3);
return false;
}
else
@ -580,13 +602,29 @@ public class Rule implements Comparable<Rule>
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall))
{
if(oneTrigger.getPhoneNumber().equals("any") | oneTrigger.getPhoneNumber().equals(PhoneStatusListener.getLastPhoneNumber()))
String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
// state dir number
if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2])))
{
if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
//if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
if(
(elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING)
||
(elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK)
||
(elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE)
)
{
if(oneTrigger.getPhoneDirection() == 0 | (oneTrigger.getPhoneDirection() == PhoneStatusListener.getLastPhoneDirection()))
if(
elements[1].equals(Trigger.triggerPhoneCallDirectionAny)
||
(elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1)
||
(elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2)
)
{
// Everything's allright
// Trigger conditions are met
}
else
{
@ -601,7 +639,10 @@ public class Rule implements Comparable<Rule>
}
}
else
{
Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4);
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag))
{
@ -648,109 +689,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 +764,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 +878,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 +894,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 +912,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)
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 +948,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 +985,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)
{
@ -963,7 +1089,7 @@ public class Rule implements Comparable<Rule>
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime())
{
Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5);
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() | searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() || searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay
{
ruleCandidates.add(oneRule);
break innerloop; //if the poi is found we don't need to search the other triggers in the same rule
@ -1234,10 +1360,10 @@ public class Rule implements Comparable<Rule>
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(boolean triggerParameter)
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(String direction)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
@ -1245,7 +1371,8 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.phoneCall)
{
if(oneTrigger.getTriggerParameter() == triggerParameter)
String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
if(elements[1].equals(Trigger.triggerPhoneCallDirectionAny) || elements[1].equals(direction))
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
@ -1253,7 +1380,7 @@ public class Rule implements Comparable<Rule>
}
}
}
return ruleCandidates;
}
@ -1279,6 +1406,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=".ActivityManageTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
@ -172,18 +178,40 @@
<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=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<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>
<activity android:name=".ActivityPermissions" />
<!-- 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"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>
</manifest>

View File

@ -1,12 +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.telephony.TelephonyManager;
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 +19,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 +28,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 +49,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 +132,7 @@ public class Rule implements Comparable<Rule>
{
return this.getName();
}
@SuppressLint("NewApi")
public String toStringLong()
{
String returnString = "";
@ -197,7 +218,7 @@ public class Rule implements Comparable<Rule>
private boolean checkBeforeSaving(Context context, boolean changeExistingRule)
{
if(this.getName() == null | this.getName().length()==0)
if(this.getName() == null || this.getName().length()==0)
{
Toast.makeText(context, context.getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false;
@ -392,13 +413,13 @@ public class Rule implements Comparable<Rule>
&&
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0
)
|
||
// Other case, start time higher than end time, timeframe goes over midnight
(
Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0
&&
(Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0
|
||
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0)
)
@ -467,7 +488,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 +496,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 +507,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 +515,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;
@ -525,12 +546,12 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for wifi state", 4);
if(oneTrigger.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected
{
if(oneTrigger.getWifiName().length() > 0) // only check if any wifi name specified, otherwise any wifi will do
if(oneTrigger.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name specified, checking that.", 4);
if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getWifiName()))
if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getTriggerParameter2()))
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getWifiName(), WifiBroadcastReceiver.getLastWifiSsid()), 3);
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), 3);
return false;
}
else
@ -578,13 +599,29 @@ public class Rule implements Comparable<Rule>
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall))
{
if(oneTrigger.getPhoneNumber().equals("any") | oneTrigger.getPhoneNumber().equals(PhoneStatusListener.getLastPhoneNumber()))
String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
// state dir number
if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2])))
{
if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
//if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
if(
(elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING)
||
(elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK)
||
(elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE)
)
{
if(oneTrigger.getPhoneDirection() == 0 | (oneTrigger.getPhoneDirection() == PhoneStatusListener.getLastPhoneDirection()))
if(
elements[1].equals(Trigger.triggerPhoneCallDirectionAny)
||
(elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1)
||
(elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2)
)
{
// Everything's allright
// Trigger conditions are met
}
else
{
@ -599,7 +636,10 @@ public class Rule implements Comparable<Rule>
}
}
else
{
Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4);
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag))
{
@ -617,109 +657,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 +732,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;
@ -743,6 +846,8 @@ public class Rule implements Comparable<Rule>
private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{
boolean wasActivated = false;
@Override
protected Void doInBackground(Object... params)
{
@ -757,7 +862,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;
}
@ -775,27 +880,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)
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);
@ -803,10 +916,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
{
@ -831,9 +953,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)
{
@ -932,7 +1057,7 @@ public class Rule implements Comparable<Rule>
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime())
{
Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5);
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() | searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() || searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay
{
ruleCandidates.add(oneRule);
break innerloop; //if the poi is found we don't need to search the other triggers in the same rule
@ -1203,10 +1328,10 @@ public class Rule implements Comparable<Rule>
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(boolean triggerParameter)
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(String direction)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
@ -1214,7 +1339,8 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.phoneCall)
{
if(oneTrigger.getTriggerParameter() == triggerParameter)
String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
if(elements[1].equals(Trigger.triggerPhoneCallDirectionAny) || elements[1].equals(direction))
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
@ -1222,7 +1348,7 @@ public class Rule implements Comparable<Rule>
}
}
}
return ruleCandidates;
}
@ -1248,6 +1374,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=".ActivityManageTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
@ -177,13 +173,34 @@
<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=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<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>
<activity android:name=".ActivityPermissions" />
<!-- 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=".receivers.ActivityDetectionReceiver"
@ -193,12 +210,14 @@
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity android:name=".ActivityPermissions"></activity>
<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,18 @@
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.telephony.TelephonyManager;
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 +21,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 +30,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,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 = "";
@ -199,7 +220,7 @@ public class Rule implements Comparable<Rule>
private boolean checkBeforeSaving(Context context, boolean changeExistingRule)
{
if(this.getName() == null | this.getName().length()==0)
if(this.getName() == null || this.getName().length()==0)
{
Toast.makeText(context, context.getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false;
@ -394,13 +415,13 @@ public class Rule implements Comparable<Rule>
&&
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0
)
|
||
// Other case, start time higher than end time, timeframe goes over midnight
(
Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0
&&
(Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0
|
||
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0)
)
@ -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;
@ -527,12 +548,12 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for wifi state", 4);
if(oneTrigger.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected
{
if(oneTrigger.getWifiName().length() > 0) // only check if any wifi name specified, otherwise any wifi will do
if(oneTrigger.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name specified, checking that.", 4);
if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getWifiName()))
if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getTriggerParameter2()))
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getWifiName(), WifiBroadcastReceiver.getLastWifiSsid()), 3);
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), 3);
return false;
}
else
@ -580,13 +601,29 @@ public class Rule implements Comparable<Rule>
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall))
{
if(oneTrigger.getPhoneNumber().equals("any") | oneTrigger.getPhoneNumber().equals(PhoneStatusListener.getLastPhoneNumber()))
String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
// state dir number
if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2])))
{
if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
//if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
if(
(elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING)
||
(elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK)
||
(elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE)
)
{
if(oneTrigger.getPhoneDirection() == 0 | (oneTrigger.getPhoneDirection() == PhoneStatusListener.getLastPhoneDirection()))
if(
elements[1].equals(Trigger.triggerPhoneCallDirectionAny)
||
(elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1)
||
(elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2)
)
{
// Everything's allright
// Trigger conditions are met
}
else
{
@ -601,7 +638,10 @@ public class Rule implements Comparable<Rule>
}
}
else
{
Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4);
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag))
{
@ -648,109 +688,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 +763,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 +877,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 +893,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 +911,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)
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 +947,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 +984,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)
{
@ -963,7 +1088,7 @@ public class Rule implements Comparable<Rule>
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime())
{
Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5);
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() | searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() || searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay
{
ruleCandidates.add(oneRule);
break innerloop; //if the poi is found we don't need to search the other triggers in the same rule
@ -1234,10 +1359,10 @@ public class Rule implements Comparable<Rule>
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(boolean triggerParameter)
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(String direction)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
@ -1245,7 +1370,8 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.phoneCall)
{
if(oneTrigger.getTriggerParameter() == triggerParameter)
String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
if(elements[1].equals(Trigger.triggerPhoneCallDirectionAny) || elements[1].equals(direction))
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
@ -1253,7 +1379,7 @@ public class Rule implements Comparable<Rule>
}
}
}
return ruleCandidates;
}
@ -1279,6 +1405,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

@ -3,6 +3,7 @@ package com.jens.automation2;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import org.apache.http.client.methods.HttpGet;
@ -12,6 +13,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 +37,7 @@ public class Action
speakText,
playMusic,
setScreenBrightness,
playSound,
sendTextMessage;
public String getFullName(Context context)
@ -87,6 +92,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 +217,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 +237,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 +335,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 +354,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();
}
}
@ -433,7 +459,7 @@ public class Action
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "triggerUrl", context.getResources().getString(R.string.errorTriggeringUrl) + ": " + e.getMessage() + ", detailed: " + Log.getStackTraceString(e), 2);
Miscellaneous.logEvent("e", "triggerUrl", context.getResources().getString(R.string.logErrorTriggeringUrl) + ": " + e.getMessage() + ", detailed: " + Log.getStackTraceString(e), 2);
}
}

File diff suppressed because it is too large Load Diff

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

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
@ -53,23 +54,25 @@ public class ActivityMainPoi extends ActivityGeneric
@Override
public void onClick(View v)
{
if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainPoi.this))
if(Miscellaneous.googleToBlameForLocation(false))
{
Toast.makeText(ActivityMainPoi.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
ActivityMainScreen.openGoogleBlamingWindow();
return;
}
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();
if (!ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, ActivityMainPoi.this) || !ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, ActivityMainPoi.this))
{
Intent permissionIntent = new Intent(ActivityMainPoi.this, ActivityPermissions.class);
permissionIntent.putExtra(ActivityPermissions.intentExtraName, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION});
startActivityForResult(permissionIntent, requestCodeForPermission);
}
else
{
buttonAddPoi();
}
}
}
});

View File

@ -50,11 +50,11 @@ public class ActivityMainProfiles extends ActivityGeneric
@Override
public void onClick(View v)
{
if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainProfiles.this))
{
Toast.makeText(ActivityMainProfiles.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
return;
}
// if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainProfiles.this))
// {
// Toast.makeText(ActivityMainProfiles.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// return;
// }
profileToEdit = null;
Intent manageSpecificProfileIntent = new Intent (ActivityMainProfiles.this, ActivityManageProfile.class);

View File

@ -52,11 +52,11 @@ public class ActivityMainRules extends ActivityGeneric
@Override
public void onClick(View v)
{
if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainRules.this))
{
Toast.makeText(ActivityMainRules.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
return;
}
// if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainRules.this))
// {
// Toast.makeText(ActivityMainRules.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// return;
// }
ruleToEdit = null;
Intent startAddRuleIntent = new Intent(ActivityMainRules.this, ActivityManageRule.class);

View File

@ -1,15 +1,17 @@
package com.jens.automation2;
import android.Manifest;
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 +31,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 +43,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 +73,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 +99,7 @@ public class ActivityMainScreen extends ActivityGeneric
}
});
tvMainScreenNote1.setOnClickListener(new OnClickListener()
tvMainScreenNotePermissions.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
@ -129,19 +133,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 +170,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 +233,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 +246,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 +324,9 @@ public class ActivityMainScreen extends ActivityGeneric
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, AutomationService.getInstance())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, Miscellaneous.getAnyContext())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, AutomationService.getInstance())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, Miscellaneous.getAnyContext())
)
activityMainScreenInstance.tvActivePoi.setText(activityMainScreenInstance.getResources().getString(R.string.stillGettingPosition));
else
@ -418,7 +401,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 +464,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 +590,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,12 +19,13 @@ 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;
protected final static int requestCodeForContactsPermissions = 9876;
protected final static int requestCodeGetContact = 3235;
// private String existingUrl = "";
@ -35,7 +36,7 @@ public class ActivityEditSendTextMessage extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.send_textmessage_editor);
this.setContentView(R.layout.activity_manage_action_send_textmessage);
etSendTextMessage = (EditText)findViewById(R.id.etSendTextMessage);
etPhoneNumber = (EditText)findViewById(R.id.etPhoneNumber);
@ -67,7 +68,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 +77,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 +100,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);
@ -145,16 +146,14 @@ public class ActivityEditSendTextMessage extends Activity
private void openContactsDialogue()
{
// Toast.makeText(ActivityEditSendTextMessage.this, "Opening contacts dialogue", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(intent, 1000);
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(intent, requestCodeGetContact);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == 1000)
if(requestCode == requestCodeGetContact)
{
if(resultCode == Activity.RESULT_OK)
{
@ -162,7 +161,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;
@ -24,7 +24,7 @@ public class ActivityEditSpeakText extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.speak_text_editor);
this.setContentView(R.layout.activity_manage_action_speak_text);
etSpeakText = (EditText)findViewById(R.id.etTextToSpeak);
bSaveSpeakText = (Button)findViewById(R.id.bSaveTriggerUrl);
@ -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;
@ -39,7 +39,7 @@ public class ActivityEditTriggerUrl extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.trigger_url_editor);
this.setContentView(R.layout.activity_manage_action_trigger_url);
etTriggerUrl = (EditText)findViewById(R.id.etTriggerUrl);
etTriggerUrlUsername = (EditText)findViewById(R.id.etTriggerUrlUsername);
@ -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

@ -47,7 +47,7 @@ public class ActivityManagePoi extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.manage_specific_poi);
this.setContentView(R.layout.activity_manage_specific_poi);
myLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
bGetPosition = (Button)findViewById(R.id.bGetPosition);
@ -172,7 +172,8 @@ public class ActivityManagePoi extends Activity
double variance = locationGps.distanceTo(locationNetwork);
String text = getResources().getString(R.string.distanceBetween) + " " + String.valueOf(Math.round(variance)) + " " + getResources().getString(R.string.radiusSuggestion);
String text = String.format(getResources().getString(R.string.distanceBetween), Math.round(variance));
// Toast.makeText(getBaseContext(), text, Toast.LENGTH_LONG).show();
Miscellaneous.logEvent("i", "POI Manager", text, 4);
// if(variance > 50 && guiPoiRadius.getText().toString().length()>0 && Integer.parseInt(guiPoiRadius.getText().toString())<variance)

View File

@ -82,7 +82,7 @@ public class ActivityManageProfile extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.manage_specific_profile);
this.setContentView(R.layout.activity_manage_specific_profile);
checkBoxChangeSoundMode = (CheckBox)findViewById(R.id.checkBoxChangeSoundMode);
checkBoxChangeVolumeMusicVideoGameMedia = (CheckBox)findViewById(R.id.checkBoxChangeVolumeMusicVideoGameMedia);

View File

@ -95,6 +95,14 @@ 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;
final static int requestCodeTriggerPhoneCallAdd = 601;
final static int requestCodeTriggerPhoneCallEdit = 602;
final static int requestCodeTriggerWifiAdd = 723;
final static int requestCodeTriggerWifiEdit = 724;
public static ActivityManageRule getInstance()
{
@ -165,7 +173,7 @@ public class ActivityManageRule extends Activity
hideKeyboard();
getActionTypeDialog().show();
}
});
});
cmdSaveRule.setOnClickListener(new OnClickListener()
{
@ -233,8 +241,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 +250,29 @@ 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;
case phoneCall:
ActivityManageTriggerPhoneCall.editedPhoneCallTrigger = selectedTrigger;
Intent phoneCallEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerPhoneCall.class);
phoneCallEditor.putExtra("edit", true);
startActivityForResult(phoneCallEditor, requestCodeTriggerPhoneCallEdit);
break;
case wifiConnection:
Intent wifiEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerWifi.class);
wifiEditor.putExtra("edit", true);
wifiEditor.putExtra("wifiState", selectedTrigger.getTriggerParameter());
wifiEditor.putExtra("wifiName", selectedTrigger.getTriggerParameter2());
startActivityForResult(wifiEditor, requestCodeTriggerWifiEdit);
break;
default:
break;
}
@ -288,57 +315,43 @@ 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);
// activityEditTriggerUrlIntent.putExtra("urlToTrigger", a.getParameter2());
ActivityEditTriggerUrl.resultingAction = a;
Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityManageActionTriggerUrl.class);
ActivityManageActionTriggerUrl.resultingAction = a;
activityEditTriggerUrlIntent.putExtra("edit", true);
startActivityForResult(activityEditTriggerUrlIntent, requestCodeActionTriggerUrlEdit);
break;
// case turnBluetoothOff:
// break;
// case turnBluetoothOn:
// break;
// case turnUsbTetheringOff:
// break;
// case turnUsbTetheringOn:
// break;
// case turnWifiOff:
// break;
// case turnWifiOn:
// break;
// case turnWifiTetheringOff:
// break;
// case turnWifiTetheringOn:
// break;
// case waitBeforeNextAction:
// break;
// 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 +479,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 +517,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;
}
@ -525,15 +548,36 @@ public class ActivityManageRule extends Activity
else if(triggerType == Trigger_Enum.speed | triggerType == Trigger_Enum.noiseLevel | triggerType == Trigger_Enum.batteryLevel)
booleanChoices = new String[]{getResources().getString(R.string.exceeds), getResources().getString(R.string.dropsBelow)};
else if(triggerType == Trigger_Enum.wifiConnection)
booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)};
{
newTrigger.setTriggerType(Trigger_Enum.wifiConnection);
Intent wifiTriggerEditor = new Intent(myContext, ActivityManageTriggerWifi.class);
startActivityForResult(wifiTriggerEditor, requestCodeTriggerWifiAdd);
return;
// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)};
}
// else if(triggerType == Trigger_Enum.wifiConnection)
// 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)
booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)};
else if(triggerType == Trigger_Enum.phoneCall)
booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)};
{
newTrigger.setTriggerType(Trigger_Enum.phoneCall);
Intent phoneTriggerEditor = new Intent(myContext, ActivityManageTriggerPhoneCall.class);
startActivityForResult(phoneTriggerEditor, requestCodeTriggerPhoneCallAdd);
return;
// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)};
}
else if(triggerType == Trigger_Enum.activityDetection)
{
try
@ -564,7 +608,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 +619,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;
}
@ -792,7 +836,7 @@ public class ActivityManageRule extends Activity
{
public void onClick(DialogInterface dialog, int whichButton)
{
newTrigger.setWifiName(input.getText().toString());
// newTrigger.setWifiName(input.getText().toString());
ruleToEdit.getTriggerSet().add(newTrigger);
refreshTriggerList();
}
@ -971,7 +1015,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 +1062,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 +1082,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 +1110,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 +1125,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,19 +1136,38 @@ 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();
}
else
Miscellaneous.logEvent("w", "TimeFrameEdit", "No timeframe returned. Assuming abort.", 5);
}
else if(requestCode == requestCodeTriggerWifiAdd)
{
if(resultCode == RESULT_OK)
{
newTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false));
newTrigger.setTriggerParameter2(data.getStringExtra("wifiName"));
ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerWifiEdit)
{
if(resultCode == RESULT_OK)
{
newTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false));
newTrigger.setTriggerParameter2(data.getStringExtra("wifiName"));
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeActionStartActivityAdd)
{
// manage start of other activity
if(resultCode == RESULT_OK)
{
newAction = ActivityManageStartActivity.resultingAction;
newAction = ActivityManageActionStartActivity.resultingAction;
ruleToEdit.getActionSet().add(newAction);
this.refreshActionList();
}
@ -1114,7 +1177,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 +1185,63 @@ 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)
{
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 if(requestCode == requestCodeTriggerNfcNotificationEdit)
{
if(resultCode == RESULT_OK)
{
newTrigger = ActivityManageTriggerNotification.resultingTrigger;
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerPhoneCallAdd)
{
if(resultCode == RESULT_OK)
{
ruleToEdit.getTriggerSet().add(newTrigger);
newTrigger.setTriggerParameter2(data.getStringExtra("triggerParameter2"));
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerPhoneCallEdit)
{
if(resultCode == RESULT_OK)
{
newTrigger = ActivityManageTriggerPhoneCall.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 +1250,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 +1268,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 +1298,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 +1373,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 +1384,161 @@ 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()))
{
//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);
}
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();
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)
Miscellaneous.messageBox(context.getResources().getString(R.string.warning), context.getResources().getString(R.string.usbTetheringFailForAboveGingerbread), context).show();
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifiTethering.toString()))
{
newAction.setAction(Action_Enum.setWifiTethering);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
Miscellaneous.messageBox(context.getResources().getString(R.string.warning), context.getResources().getString(R.string.wifiTetheringFailForAboveNougat), context).show();
else
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString()))
}
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)
{
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();
newAction.setAction(Action_Enum.changeSoundProfile);
getActionSoundProfileDialog(context).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setUsbTethering.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()))
{
newAction.setAction(Action_Enum.setAirplaneMode);
getActionParameter1Dialog(ActivityManageRule.this).show();
if(Build.VERSION.SDK_INT >= 17)
{
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();
// 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.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()))
{
}
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;
@ -29,7 +29,7 @@ public class ActivityManageBluetoothTrigger extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_trigger);
setContentView(R.layout.activity_manage_trigger_bluetooth);
radioAnyBluetoothDevice = (RadioButton)findViewById(R.id.radioAnyBluetoothDevice);
radioNoDevice = (RadioButton)findViewById(R.id.radioNoDevice);
@ -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,398 @@
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;
boolean edit = false;
ProgressDialog progressDialog = null;
EditText etNotificationTitle, etNotificationText;
Button bSelectApp, bSaveTriggerNotification;
Spinner spinnerTitleDirection, spinnerTextDirection;
TextView tvSelectedApplication;
CheckBox chkNotificationDirection;
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.activity_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

@ -0,0 +1,212 @@
package com.jens.automation2;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import androidx.annotation.NonNull;
import static com.jens.automation2.Trigger.triggerParameter2Split;
public class ActivityManageTriggerPhoneCall extends Activity
{
public static Trigger editedPhoneCallTrigger;
boolean edit = false;
public static Trigger resultingTrigger;
ProgressDialog progressDialog = null;
protected final static int requestCodeForContactsPermissions = 2345;
protected final static int requestCodeGetContact = 3235;
EditText etTriggerPhoneCallPhoneNumber;
RadioButton rbTriggerPhoneCallStateRinging, rbTriggerPhoneCallStateStarted, rbTriggerPhoneCallStateStopped, rbTriggerPhoneCallDirectionAny, rbTriggerPhoneCallDirectionIncoming, rbTriggerPhoneCallDirectionOutgoing;
Button bSaveTriggerPhoneCall, bTriggerPhoneCallImportFromContacts;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_trigger_phone_call);
etTriggerPhoneCallPhoneNumber = (EditText)findViewById(R.id.etTriggerPhoneCallPhoneNumber);
rbTriggerPhoneCallStateRinging = (RadioButton)findViewById(R.id.rbTriggerPhoneCallStateRinging);
rbTriggerPhoneCallStateStarted = (RadioButton)findViewById(R.id.rbTriggerPhoneCallStateStarted);
rbTriggerPhoneCallStateStopped = (RadioButton)findViewById(R.id.rbTriggerPhoneCallStateStopped);
rbTriggerPhoneCallDirectionAny = (RadioButton)findViewById(R.id.rbTriggerPhoneCallDirectionAny);
rbTriggerPhoneCallDirectionIncoming = (RadioButton)findViewById(R.id.rbTriggerPhoneCallDirectionIncoming);
rbTriggerPhoneCallDirectionOutgoing = (RadioButton)findViewById(R.id.rbTriggerPhoneCallDirectionOutgoing);
bTriggerPhoneCallImportFromContacts = (Button) findViewById(R.id.bTriggerPhoneCallImportFromContacts);
bSaveTriggerPhoneCall = (Button) findViewById(R.id.bSaveTriggerPhoneCall);
bSaveTriggerPhoneCall.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
String tp2Result = "";
if(rbTriggerPhoneCallStateRinging.isChecked())
tp2Result += Trigger.triggerPhoneCallStateRinging;
else if(rbTriggerPhoneCallStateStarted.isChecked())
tp2Result += Trigger.triggerPhoneCallStateStarted;
else if(rbTriggerPhoneCallStateStopped.isChecked())
tp2Result += Trigger.triggerPhoneCallStateStopped;
tp2Result += triggerParameter2Split;
if(rbTriggerPhoneCallDirectionAny.isChecked())
tp2Result += Trigger.triggerPhoneCallDirectionAny;
else if(rbTriggerPhoneCallDirectionIncoming.isChecked())
tp2Result += Trigger.triggerPhoneCallDirectionIncoming;
else if(rbTriggerPhoneCallDirectionOutgoing.isChecked())
tp2Result += Trigger.triggerPhoneCallDirectionOutgoing;
tp2Result += triggerParameter2Split;
if(etTriggerPhoneCallPhoneNumber.getText() != null && etTriggerPhoneCallPhoneNumber.getText().toString().length() > 0)
tp2Result += etTriggerPhoneCallPhoneNumber.getText().toString();
else
tp2Result += Trigger.triggerPhoneCallNumberAny;
if(edit)
{
editedPhoneCallTrigger.setTriggerParameter(false);
editedPhoneCallTrigger.setTriggerParameter2(tp2Result);
ActivityManageTriggerPhoneCall.this.setResult(RESULT_OK);
}
else
{
Intent data = new Intent();
data.putExtra("triggerParameter", false);
data.putExtra("triggerParameter2", tp2Result);
ActivityManageTriggerPhoneCall.this.setResult(RESULT_OK, data);
}
finish();
}
});
bTriggerPhoneCallImportFromContacts.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !ActivityPermissions.havePermission("android.permission.READ_CONTACTS", ActivityManageTriggerPhoneCall.this))
{
requestPermissions("android.permission.READ_CONTACTS");
}
else
openContactsDialogue();
}
});
Intent i = getIntent();
if(i.getBooleanExtra("edit", false) == true)
{
edit = true;
loadValuesIntoGui();
}
}
protected void requestPermissions(String... requiredPermissions)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if(requiredPermissions.length > 0)
{
StringBuilder permissions = new StringBuilder();
for (String perm : requiredPermissions)
permissions.append(perm + "; ");
if (permissions.length() > 0)
permissions.delete(permissions.length() - 2, permissions.length());
Miscellaneous.logEvent("i", "Permissions", "Requesting permissions: " + permissions, 2);
requestPermissions(requiredPermissions, requestCodeForContactsPermissions);
}
}
}
private void openContactsDialogue()
{
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(intent, requestCodeGetContact);
}
private void loadValuesIntoGui()
{
String[] parts = editedPhoneCallTrigger.getTriggerParameter2().split(triggerParameter2Split);
if(parts[0].equals(Trigger.triggerPhoneCallStateRinging))
rbTriggerPhoneCallStateRinging.setChecked(true);
else if(parts[0].equals(Trigger.triggerPhoneCallStateStarted))
rbTriggerPhoneCallStateStarted.setChecked(true);
else if(parts[0].equals(Trigger.triggerPhoneCallStateStopped))
rbTriggerPhoneCallStateStopped.setChecked(true);
if(parts[1].equals(Trigger.triggerPhoneCallDirectionAny))
rbTriggerPhoneCallDirectionAny.setChecked(true);
else if(parts[1].equals(Trigger.triggerPhoneCallDirectionIncoming))
rbTriggerPhoneCallDirectionIncoming.setChecked(true);
else if(parts[1].equals(Trigger.triggerPhoneCallDirectionOutgoing))
rbTriggerPhoneCallDirectionOutgoing.setChecked(true);
if(!parts[2].equals(Trigger.triggerPhoneCallNumberAny))
etTriggerPhoneCallPhoneNumber.setText(parts[2]);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == requestCodeGetContact)
{
if(resultCode == Activity.RESULT_OK)
{
String phoneNo = null;
String name = null;
Uri uri = data.getData();
Cursor cursor = ActivityManageTriggerPhoneCall.this.getContentResolver().query(uri, null, null, null, null);
if (cursor.moveToFirst())
{
int phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
phoneNo = cursor.getString(phoneIndex);
name = cursor.getString(nameIndex);
etTriggerPhoneCallPhoneNumber.setText(phoneNo);
}
}
}
//super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
if(requestCode == requestCodeForContactsPermissions)
{
for(int i=0; i<permissions.length; i++)
{
if(permissions[i].equals("android.permission.READ_CONTACTS"))
{
if(grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
openContactsDialogue();
}
}
}
}
}
}

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.activity_manage_trigger_timeframe);
startPicker = (TimePicker)findViewById(R.id.tpTimeFrameStart);
stopPicker = (TimePicker)findViewById(R.id.tpTimeFrameStop);

View File

@ -0,0 +1,163 @@
package com.jens.automation2;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import com.jens.automation2.receivers.BluetoothReceiver;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ActivityManageTriggerWifi extends Activity
{
RadioButton rbTriggerWifiConnected, rbTriggerWifiDisconnected;
EditText etTriggerWifiName;
Spinner spinnerWifiList;
Button btriggerWifiSave, bLoadWifiList;
List<String> wifiList = new ArrayList<>();
ArrayAdapter<String> wifiSpinnerAdapter;
private final static int requestCodeLocationPermission = 124;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_trigger_wifi);
rbTriggerWifiConnected = (RadioButton) findViewById(R.id.rbTriggerWifiConnected);
rbTriggerWifiDisconnected = (RadioButton) findViewById(R.id.rbTriggerWifiDisconnected);
etTriggerWifiName = (EditText) findViewById(R.id.etTriggerWifiName);
spinnerWifiList = (Spinner) findViewById(R.id.spinnerWifiList);
btriggerWifiSave = (Button) findViewById(R.id.btriggerWifiSave);
bLoadWifiList = (Button) findViewById(R.id.bLoadWifiList);
wifiSpinnerAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, wifiList);
spinnerWifiList.setAdapter(wifiSpinnerAdapter);
if (getIntent().hasExtra("edit"))
{
boolean connected = getIntent().getBooleanExtra("wifiState", false);
String wifiName = getIntent().getStringExtra("wifiName");
rbTriggerWifiConnected.setChecked(connected);
rbTriggerWifiDisconnected.setChecked(!connected);
etTriggerWifiName.setText(wifiName);
}
btriggerWifiSave.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent response = new Intent();
response.putExtra("wifiState", rbTriggerWifiConnected.isChecked());
response.putExtra("wifiName", etTriggerWifiName.getText().toString());
setResult(RESULT_OK, response);
finish();
}
});
spinnerWifiList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
etTriggerWifiName.setText(wifiList.get(position));
}
@Override
public void onNothingSelected(AdapterView<?> parent)
{
}
});
bLoadWifiList.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
loadWifis();
}
});
}
public void loadWifis()
{
if(!ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, ActivityManageTriggerWifi.this))
{
AlertDialog dialog = Miscellaneous.messageBox(getResources().getString(R.string.permissionsTitle), getResources().getString(R.string.needLocationPermForWifiList), ActivityManageTriggerWifi.this);
dialog.setOnDismissListener(new DialogInterface.OnDismissListener()
{
@Override
public void onDismiss(DialogInterface dialog)
{
ActivityCompat.requestPermissions(ActivityManageTriggerWifi.this, new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, requestCodeLocationPermission);
}
});
dialog.show();
}
else
{
reallyLoadWifiList();
}
}
void reallyLoadWifiList()
{
WifiManager myWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
for (WifiConfiguration wifi : myWifiManager.getConfiguredNetworks())
wifiList.add(wifi.SSID.replaceAll("\"+$", "").replaceAll("^\"+", ""));
if(wifiList.size() > 0)
{
spinnerWifiList.setEnabled(true);
Collections.sort(wifiList);
}
else
{
spinnerWifiList.setEnabled(false);
Toast.makeText(ActivityManageTriggerWifi.this, getResources().getString(R.string.noKnownWifis), Toast.LENGTH_SHORT).show();
}
wifiSpinnerAdapter.notifyDataSetChanged();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case requestCodeLocationPermission:
if(grantResults[0] == PackageManager.PERMISSION_GRANTED)
reallyLoadWifiList();
break;
}
}
}

View File

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
@ -37,9 +38,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;
@ -106,28 +108,28 @@ public class AutomationService extends Service implements OnInitListener
public boolean checkStartupRequirements(Context context, boolean startAtBoot)
{
if (!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, AutomationService.this))
{
/*
Don't have permission to access external storage. This is a show stopper as
the configuration file is stored on external storage.
*/
Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4);
// Toast.makeText(AutomationService.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName);
return false;
}
// if (!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, AutomationService.this))
// {
// /*
// Don't have permission to access external storage. This is a show stopper as
// the configuration file is stored on external storage.
// */
// Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4);
//// Toast.makeText(AutomationService.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName);
// return false;
// }
if(Build.VERSION.SDK_INT >= 28)
{
if (!ActivityPermissions.havePermission(ActivityPermissions.permissionNameStartService, AutomationService.this))
if (!ActivityPermissions.havePermission(Manifest.permission.FOREGROUND_SERVICE, AutomationService.this))
{
/*
Don't have permission to start service. This is a show stopper.
*/
Miscellaneous.logEvent("e", "Permission", "Don't have permission to start foreground service. Will request it now.", 4);
// Toast.makeText(AutomationService.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
ActivityPermissions.requestSpecificPermission(ActivityPermissions.permissionNameStartService);
ActivityPermissions.requestSpecificPermission(Manifest.permission.FOREGROUND_SERVICE);
return false;
}
}
@ -154,7 +156,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 +172,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 +319,7 @@ public class AutomationService extends Service implements OnInitListener
checkForTtsEngine();
checkForPermissions();
checkForRestrictedFeatures();
checkForMissingBackgroundLocationPermission();
Actions.context = this;
Actions.autoMationServerRef = this;
@ -365,7 +367,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 +390,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)))
@ -407,7 +446,8 @@ public class AutomationService extends Service implements OnInitListener
private void stopRoutine()
{
Log.i("STOP", "Stopping");
Miscellaneous.logEvent("i", "Service", "Stopping service...", 3);
// Log.i("STOP", "Stopping");
try
{
myLocationProvider.stopLocationService();
@ -543,11 +583,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)
@ -555,9 +595,9 @@ public class AutomationService extends Service implements OnInitListener
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, AutomationService.getInstance())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, AutomationService.getInstance())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, AutomationService.getInstance())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, AutomationService.getInstance())
)
bodyText = instance.getResources().getString(R.string.stillGettingPosition);
else

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

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Notification;
@ -7,6 +8,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,18 +16,20 @@ 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;
import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
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 +48,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 +79,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 +93,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");
@ -313,45 +329,74 @@ public class Miscellaneous extends Service
{
if(writeableFolderStringCache == null)
{
String testPath = null;
File folder = null;
// Use the app-specific folder as new default.
writeableFolderStringCache = Miscellaneous.getAnyContext().getFilesDir().getAbsolutePath();
try
File newConfigFile = new File(writeableFolderStringCache + "/" + XmlFileInterface.settingsFileName);
migration:
if (!newConfigFile.exists())
{
String[] foldersToTestArray = new String[]
{
Environment.getExternalStorageDirectory().getAbsolutePath(),
"/storage/emulated/0",
"/HWUserData",
"/mnt/sdcard"
};
for(String f : foldersToTestArray)
if (ActivityPermissions.havePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Miscellaneous.getAnyContext()))
{
if (testFolder(f))
// We have the storage permission, probably because it's an old installation. Files should be migrated to app-specific folder.
String testPath = null;
File folder = null;
try
{
String pathToUse = f + "/" + Settings.folderName;
Miscellaneous.logEvent("i", "Path", "Using " + pathToUse + " to store settings and log.", 2);
String[] foldersToTestArray = new String[]
{
Environment.getExternalStorageDirectory().getAbsolutePath(),
"/storage/emulated/0",
"/HWUserData",
"/mnt/sdcard"
};
for (String f : foldersToTestArray)
{
// if (testFolder(f))
// {
String pathToUse = f + "/" + Settings.folderName;
// Toast.makeText(getAnyContext(), "Using " + pathToUse + " to store settings and log.", Toast.LENGTH_LONG).show();
return pathToUse;
// Migrate existing files
File oldDirectory = new File(pathToUse);
File newDirectory = new File(writeableFolderStringCache);
File oldConfigFilePath = new File(pathToUse + "/" + XmlFileInterface.settingsFileName);
if (oldConfigFilePath.exists() && oldConfigFilePath.canWrite())
{
Miscellaneous.logEvent("i", "Path", "Found old path " + pathToUse + " for settings and logs. Migrating old files to new directory.", 2);
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);
break migration;
}
// }
}
} catch (Exception e)
{
Log.w("getWritableFolder", folder + " not writable.");
}
else
Miscellaneous.logEvent("e", "getWritableFolder", folder.getAbsolutePath() + " does not exist and could not be created.", 3);
}
}
catch(Exception e)
{
Log.w("getWritableFolder", folder + " not writable.");
}
// do not change to logEvent() - we can't write
Toast.makeText(getAnyContext(), "No writable folder could be found.", Toast.LENGTH_LONG).show();
Log.e("getWritableFolder", "No writable folder could be found.");
return null;
}
else
return writeableFolderStringCache;
return writeableFolderStringCache;
}
protected final static String logFileName = "Automation_logfile.txt";
@ -408,6 +453,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)
{
@ -536,6 +600,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);
@ -735,10 +819,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);
@ -750,50 +887,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)
{
@ -803,7 +968,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);
@ -833,13 +998,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)
@ -1037,4 +1210,295 @@ 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;
}
public static boolean isRegularExpression(String regex)
{
try
{
"compareString".matches(regex); //will cause expection if no valid regex
return true;
}
catch(java.util.regex.PatternSyntaxException e)
{
}
return false;
}
public static boolean comparePhoneNumbers(String number1, String number2)
{
/* To be activated when Android S SDK comes out
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.Q)
{
TelephonyManager tm = (TelephonyManager)Miscellaneous.getAnyContext().getSystemService(Context.TELEPHONY_SERVICE);
return PhoneNumberUtils.areSamePhoneNumber(number1, number2, tm.getNetworkCountryIso());
}
else*/
return PhoneNumberUtils.compare(number1, number2);
}
}

View File

@ -10,10 +10,8 @@ import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
@ -70,9 +68,15 @@ public class News
Calendar now = Calendar.getInstance();
String newsContent;
String filePath = context.getFilesDir() + "/appNews.xml";
String newsFileName = "appNews.xml";
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.pollNewsEveryXDays * 24 * 60 * 60 * 1000))
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))
{
String newsUrl = "https://server47.de/automation/appNews.php";
newsContent = Miscellaneous.downloadURL(newsUrl, null, null);
@ -82,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<>();
@ -245,7 +249,7 @@ public class News
try
{
Calendar limit = Calendar.getInstance();
limit.add(Calendar.DAY_OF_MONTH, -Settings.pollNewsEveryXDays);
limit.add(Calendar.DAY_OF_MONTH, -Settings.newsPollEveryXDays);
return downloadNews(contexts[0], limit);
}
catch(Exception e)
@ -258,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

@ -491,9 +491,17 @@ public class PointOfInterest implements Comparable<PointOfInterest>
AutomationService service = AutomationService.getInstance();
if (service != null)
{
service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
try
{
service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
}
catch(Exception e)
{
// Just log the event. This should not cause an interruption in the program flow.
Miscellaneous.logEvent("e", "save POI", "Error when trying to apply settings and rules: " + Log.getStackTraceString(e), 2);
}
}
return true;
@ -530,14 +538,18 @@ public class PointOfInterest implements Comparable<PointOfInterest>
PointOfInterest.writePoisToFile();
AutomationService service = AutomationService.getInstance();
if(service != null)
try
{
service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
}
catch(Exception e)
{
// Just log the event. This should not cause an interruption in the program flow.
Miscellaneous.logEvent("e", "save POI", "Error when trying to apply settings and rules: " + Log.getStackTraceString(e), 2);
}
return true;
}

View File

@ -56,7 +56,7 @@ public class ReceiverCoordinator
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
// e.printStackTrace();
allImplementers = new Class[] {
AlarmListener.class,
@ -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))
@ -268,34 +271,46 @@ public class ReceiverCoordinator
ProcessListener.stopProcessListener(AutomationService.getInstance());
}
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor"))
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
{
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);
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if (runResult instanceof Boolean)
{
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(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
}
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);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
else
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "stopActivityDetectionReceiver", null);
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if (runResult instanceof Boolean)
{
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;
@ -12,8 +13,12 @@ public class Settings implements SharedPreferences
{
public static final int rulesThatHaveBeenRanHistorySize = 10;
public final static int lockSoundChangesInterval = 15;
public static final int pollNewsEveryXDays = 7;
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;
@ -61,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
*/
@ -106,7 +113,7 @@ public class Settings implements SharedPreferences
protected static final boolean default_privacyLocationing = false;
protected static final int default_startScreen = 0;
protected static final boolean default_executeRulesAndProfilesWithSingleClick = false;
protected static final boolean default_displayNewsOnMainScreen = true;
protected static final boolean default_displayNewsOnMainScreen = false;
protected static final boolean default_lockSoundChanges = false;
protected static final long default_lastNewsPolltime = -1;
@ -249,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)
{
@ -260,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.**/
@ -409,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();
@ -479,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,15 +69,25 @@ 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;
private TimeFrame timeFrame;
public static String triggerPhoneCallStateRinging = "ringing";
public static String triggerPhoneCallStateStarted = "started";
public static String triggerPhoneCallStateStopped = "stopped";
public static String triggerPhoneCallDirectionIncoming = "incoming";
public static String triggerPhoneCallDirectionOutgoing = "outgoing";
public static String triggerPhoneCallDirectionAny = "any";
public static String triggerPhoneCallNumberAny = "any";
private double speed; //km/h
private long noiseLevelDb;
private String wifiName = "";
private String processName = null;
private String processName = null;
private int batteryLevel;
private int phoneDirection = 0; // 0=any, 1=incoming, 2=outgoing
private String phoneNumber = null;
@ -206,6 +218,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 +238,6 @@ public class Trigger
this.timeFrame = timeFrame;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@SuppressWarnings("unused")
@Override
@ -292,10 +313,10 @@ public class Trigger
break;
case wifiConnection:
String wifiDisplayName = "";
if(this.getWifiName().length() == 0)
if(this.getTriggerParameter2().length() == 0)
wifiDisplayName += Miscellaneous.getAnyContext().getResources().getString(R.string.anyWifi);
else
wifiDisplayName += this.getWifiName();
wifiDisplayName += this.getTriggerParameter2();
if(getTriggerParameter())
returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.connectedToWifi), wifiDisplayName));
@ -325,21 +346,45 @@ public class Trigger
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.roaming));
break;
case phoneCall:
if(getPhoneDirection() == 1)
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.incomingAdjective) + " ");
else if(getPhoneDirection() == 2)
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingAdjective) + " ");
String[] elements = triggerParameter2.split(triggerParameter2Split);
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.phoneCall));
if(phoneNumber != null && !phoneNumber.equals("any"))
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.with) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.number) + " " + phoneNumber);
returnString.append(" ");
if(elements[1].equals(triggerPhoneCallDirectionAny))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.with));
else if(elements[1].equals(triggerPhoneCallDirectionIncoming))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.from));
else if(elements[1].equals(triggerPhoneCallDirectionOutgoing))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.to));
returnString.append(" ");
if(elements[2].equals(Trigger.triggerPhoneCallNumberAny))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.any) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.number));
else
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.with) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.anyNumber));
if(getTriggerParameter())
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.started));
else
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.stopped));
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.number) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.matching) + " " + elements[2]);
returnString.append(" ");
if(elements[0].equals(Trigger.triggerPhoneCallStateRinging))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.ringing));
else if(elements[0].equals(Trigger.triggerPhoneCallStateStarted))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.started));
else if(elements[0].equals(Trigger.triggerPhoneCallStateStopped))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.stopped));
// returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.phoneCall));
// if(phoneNumber != null && !phoneNumber.equals("any"))
// returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.with) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.number) + " " + phoneNumber);
// else
// returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.with) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.anyNumber));
//
// if(getTriggerParameter())
// returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.started));
// else
// returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.stopped));
break;
case nfcTag:
// This type doesn't have an activate/deactivate equivalent
@ -440,6 +485,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 +532,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()
{
@ -478,15 +603,6 @@ public class Trigger
return (String[])triggerTypesList.toArray(new String[triggerTypesList.size()]);
}
public String getWifiName()
{
return wifiName;
}
public void setWifiName(String wifiName)
{
this.wifiName = wifiName;
}
public void setBluetoothEvent(String string)
{
this.bluetoothEvent = string;

View File

@ -23,9 +23,12 @@ import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import static com.jens.automation2.Trigger.triggerParameter2Split;
public class XmlFileInterface
{
public static File settingsFile = new File(Miscellaneous.getWriteableFolder() + "/Automation_settings.xml");
public static String settingsFileName = "Automation_settings.xml";
public static File settingsFile = new File(Miscellaneous.getWriteableFolder() + "/" + settingsFileName);
public static Context context;
protected static final String encryptionKey = "Y1vsP12L2S3NkTJbDOR4bQ6i02hsoo";
@ -252,13 +255,13 @@ public class XmlFileInterface
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.noiseLevel)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getNoiseLevelDb()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.wifiConnection)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getWifiName());
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerParameter2());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.process_started_stopped)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getProcessName());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.batteryLevel)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getBatteryLevel()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.phoneCall)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneDirection()) + "," + String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneNumber()));
// else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.phoneCall)
// serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneDirection()) + "," + String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneNumber()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.nfcTag)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getNfcTagId());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.activityDetection)
@ -270,6 +273,10 @@ 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()));
else
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerParameter2()));
serializer.endTag(null, "TriggerParameter2");
serializer.endTag(null, "Trigger");
}
@ -344,17 +351,22 @@ public class XmlFileInterface
public static void readFile() throws FileNotFoundException
{
if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, Miscellaneous.getAnyContext()))
{
/*
Don't have permission to access external storage. This is a show stopper as
the configuration file is stored on external storage.
*/
Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4);
Toast.makeText(Miscellaneous.getAnyContext(), Miscellaneous.getAnyContext().getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName);
return;
}
/*
Storage location has been moved to app-specific folder in Android/data
Hence this permission is not requested any more. If it is already granted we assume the files are on /sdcard or similar.
Migration to app-specific folder has yet to be implemented.
*/
// if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, Miscellaneous.getAnyContext()))
// {
// /*
// Don't have permission to access external storage. This is a show stopper as
// the configuration file is stored on external storage.
// */
// Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4);
// Toast.makeText(Miscellaneous.getAnyContext(), Miscellaneous.getAnyContext().getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName);
// return;
// }
/*
If we are here it may be that we just got permission to read storage. We need to check for the
@ -371,7 +383,7 @@ public class XmlFileInterface
}
catch (XmlPullParserException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch(FileNotFoundException e)
{
@ -386,12 +398,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)
{
@ -526,11 +538,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
@ -743,13 +755,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"))
@ -760,13 +770,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
@ -857,38 +865,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"))
{
@ -911,42 +892,91 @@ public class XmlFileInterface
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 2);
Toast.makeText(context, "Error while writing file: " + Log.getStackTraceString(e), Toast.LENGTH_LONG).show();
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.timeFrame)
{
newTrigger.setTimeFrame(new TimeFrame(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.batteryLevel)
{
newTrigger.setBatteryLevel(Integer.parseInt(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.speed)
{
newTrigger.setSpeed(Double.parseDouble(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.noiseLevel)
{
newTrigger.setNoiseLevelDb(Long.parseLong(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.wifiConnection)
{
newTrigger.setWifiName(triggerParameter2);
// newTrigger.setWifiName(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.process_started_stopped)
{
newTrigger.setProcessName(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.phoneCall)
{
// 0/1/2,number
int direction = Integer.parseInt(triggerParameter2.substring(0, 1));
String number = triggerParameter2.substring(2);
newTrigger.setPhoneDirection(direction);
newTrigger.setPhoneNumber(number);
String[] elements = triggerParameter2.split(",");
if(elements.length == 2) //old format
{
// 0/1/2,number
int direction = Integer.parseInt(elements[0]);
String number = elements[1];
newTrigger.setPhoneDirection(direction);
newTrigger.setPhoneNumber(number);
String tp2String = "";
if(newTrigger.getTriggerParameter())
tp2String+= Trigger.triggerPhoneCallStateStarted;
else
tp2String+= Trigger.triggerPhoneCallStateStopped;
tp2String += triggerParameter2Split;
switch(direction)
{
case 0:
tp2String += Trigger.triggerPhoneCallDirectionAny;
break;
case 1:
tp2String += Trigger.triggerPhoneCallDirectionIncoming;
break;
case 2:
tp2String += Trigger.triggerPhoneCallDirectionOutgoing;
break;
}
tp2String += triggerParameter2Split;
tp2String += number;
newTrigger.setTriggerParameter2(tp2String);
}
/*else // new format
{
//tp1 is now irrelevant
elements = triggerParameter2.split(Trigger.triggerParameter2Split);
// state/direction/number
}*/
else
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.nfcTag)
{
newTrigger.setNfcTagId(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.activityDetection)
{
@ -958,6 +988,7 @@ public class XmlFileInterface
{
newTrigger.setActivityDetectionType(0);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.bluetoothConnection)
{
@ -967,6 +998,7 @@ public class XmlFileInterface
newTrigger.setBluetoothEvent(substrings[0]);
newTrigger.setBluetoothDeviceAddress(substrings[1]);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.headsetPlugged)
{
@ -978,7 +1010,10 @@ public class XmlFileInterface
{
newTrigger.setHeadphoneType(-1);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else
newTrigger.setTriggerParameter2(triggerParameter2);
}
else
{
@ -1066,19 +1101,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);
@ -1099,29 +1124,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"))
{
@ -1209,6 +1214,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

@ -0,0 +1,19 @@
package com.jens.automation2.actions.wifi_router;
/*
Class taken from here:
https://github.com/aegis1980/WifiHotSpot
*/
public abstract class MyOnStartTetheringCallback
{
/**
* Called when tethering has been successfully started.
*/
public abstract void onTetheringStarted();
/**
* Called when starting tethering failed.
*/
public abstract void onTetheringFailed();
}

View File

@ -0,0 +1,205 @@
package com.jens.automation2.actions.wifi_router;
/*
Class taken from here:
https://github.com/aegis1980/WifiHotSpot
*/
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.android.dx.stock.ProxyBuilder;
import com.jens.automation2.Miscellaneous;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Created by jonro on 19/03/2018.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public class MyOreoWifiManager
{
private static final String TAG = MyOreoWifiManager.class.getSimpleName();
private Context mContext;
private WifiManager mWifiManager;
private ConnectivityManager mConnectivityManager;
public MyOreoWifiManager(Context c)
{
mContext = c;
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mConnectivityManager = (ConnectivityManager) mContext.getSystemService(ConnectivityManager.class);
}
/**
* This sets the Wifi SSID and password
* Call this before {@code startTethering} if app is a system/privileged app
* Requires: android.permission.TETHER_PRIVILEGED which is only granted to system apps
*/
public void configureHotspot(String name, String password)
{
WifiConfiguration apConfig = new WifiConfiguration();
apConfig.SSID = name;
apConfig.preSharedKey = password;
apConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
try
{
Method setConfigMethod = mWifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class);
boolean status = (boolean) setConfigMethod.invoke(mWifiManager, apConfig);
Miscellaneous.logEvent("i", "configureHotspot()", "setWifiApConfiguration - success? " + status, 2);
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "configureHotspot()", "Error in configureHotspot: " + Log.getStackTraceString(e), 2);
}
}
/**
* Checks where tethering is on.
* This is determined by the getTetheredIfaces() method,
* that will return an empty array if not devices are tethered
*
* @return true if a tethered device is found, false if not found
*/
public boolean isTetherActive()
{
try
{
Method method = mConnectivityManager.getClass().getDeclaredMethod("getTetheredIfaces");
if (method == null)
{
Miscellaneous.logEvent("i", "getTetheredIfaces()", "getTetheredIfaces is null", 2);
}
else
{
String res[] = (String []) method.invoke(mConnectivityManager, null);
Miscellaneous.logEvent("i", "isTetherActive()", "getTetheredIfaces invoked", 5);
Miscellaneous.logEvent("i", "isTetherActive()", Arrays.toString(res), 4);
if (res.length > 0)
{
return true;
}
}
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "isTetherActive()", "Error in getTetheredIfaces: " + Log.getStackTraceString(e), 2);
}
return false;
}
/**
* This enables tethering using the ssid/password defined in Settings App>Hotspot & tethering
* Does not require app to have system/privileged access
* Credit: Vishal Sharma - https://stackoverflow.com/a/52219887
*/
public boolean startTethering(final MyOnStartTetheringCallback callback)
{
// On Pie if we try to start tethering while it is already on, it will
// be disabled. This is needed when startTethering() is called programmatically.
if (isTetherActive())
{
Miscellaneous.logEvent("i", "startTethering()", "Tether already active, returning", 2);
return false;
}
File outputDir = mContext.getCodeCacheDir();
Object proxy;
try
{
proxy = ProxyBuilder.forClass(OnStartTetheringCallbackClass())
.dexCache(outputDir).handler(new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
switch (method.getName())
{
case "onTetheringStarted":
callback.onTetheringStarted();
break;
case "onTetheringFailed":
callback.onTetheringFailed();
break;
default:
ProxyBuilder.callSuper(proxy, method, args);
}
return null;
}
}).build();
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "startTethering()", "Error in enableTethering ProxyBuilder", 2);
return false;
}
Method method = null;
try
{
method = mConnectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, OnStartTetheringCallbackClass(), Handler.class);
if (method == null)
{
Miscellaneous.logEvent("w", "startTethering()", "startTetheringMethod is null", 2);
}
else
{
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE, false, proxy, null);
Miscellaneous.logEvent("i", "startTethering()", "startTethering invoked", 5);
}
return true;
}
catch (Exception e)
{
Miscellaneous.logEvent("w", "startTethering()", "Error in enableTethering: " + Log.getStackTraceString(e), 2);
}
return false;
}
public void stopTethering()
{
try
{
Method method = mConnectivityManager.getClass().getDeclaredMethod("stopTethering", int.class);
if (method == null)
{
Miscellaneous.logEvent("w", "stopTethering", "stopTetheringMethod is null", 2);
}
else
{
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE);
Miscellaneous.logEvent("i", "stopTethering", "stopTethering invoked", 5);
}
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "stopTethering", "stopTethering error: " + Log.getStackTraceString(e), 1);
}
}
private Class OnStartTetheringCallbackClass()
{
try
{
return Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback");
}
catch (ClassNotFoundException e)
{
Miscellaneous.logEvent("e", "OnStartTetheringCallbackClass()", "OnStartTetheringCallbackClass error: " + Log.getStackTraceString(e), 1);
}
return null;
}
}

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

@ -10,6 +10,7 @@ import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.PointOfInterest;
import com.jens.automation2.R;
@ -28,9 +29,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
protected static WifiBroadcastReceiver wifiBrInstance;
protected static IntentFilter wifiListenerIntentFilter;
protected static boolean wifiListenerActive=false;
public static String getLastWifiSsid()
{
return lastWifiSsid;
@ -103,7 +102,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
Miscellaneous.logEvent("i", "WifiReceiver", context.getResources().getString(R.string.poiHasNoWifiNotStoppingCellLocationListener), 2);
}
findRules(parentLocationProvider);
findRules(AutomationService.getInstance());
}
else if(myWifi.isConnectedOrConnecting()) // first time connect from wifi-listener-perspective
{
@ -115,7 +114,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
String ssid = myWifiManager.getConnectionInfo().getSSID();
setLastWifiSsid(ssid);
lastConnectedState = true;
findRules(parentLocationProvider);
findRules(AutomationService.getInstance());
}
else if(!myWifi.isConnectedOrConnecting()) // really disconnected? because sometimes also fires on connect
{
@ -128,7 +127,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
mayCellLocationChangedReceiverBeActivatedFromWifiPointOfWifi = true;
CellLocationChangedReceiver.startCellLocationChangedReceiver();
lastConnectedState = false;
findRules(parentLocationProvider);
findRules(AutomationService.getInstance());
}
catch(Exception e)
{
@ -143,13 +142,13 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
}
}
public static void findRules(LocationProvider parentLocationProvider)
public static void findRules(AutomationService automationServiceInstance)
{
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByWifiConnection();
for(Rule oneRule : ruleCandidates)
{
if(oneRule.applies(parentLocationProvider.parentService))
oneRule.activate(parentLocationProvider.parentService, false);
if(oneRule.applies(automationServiceInstance))
oneRule.activate(automationServiceInstance, false);
}
}

View File

@ -161,7 +161,7 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
WifiBroadcastReceiver.setLastWifiSsid(wifiInfo.getSSID());
WifiBroadcastReceiver.findRules(automationServiceRef.getLocationProvider());
WifiBroadcastReceiver.findRules(automationServiceRef);
break;
case ConnectivityManager.TYPE_MOBILE:
boolean isRoaming = isRoaming(context);
@ -219,7 +219,7 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
// This will serve as a disconnected event. Happens if wifi is connected, then module deactivated.
Miscellaneous.logEvent("i", "Connectivity", "Wifi deactivated while having been connected before.", 4);
WifiBroadcastReceiver.lastConnectedState = false;
WifiBroadcastReceiver.findRules(automationServiceRef.getLocationProvider());
WifiBroadcastReceiver.findRules(automationServiceRef);
}
}
}

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

View File

@ -1,5 +1,7 @@
package com.jens.automation2.receivers;
import android.Manifest;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -13,16 +15,18 @@ import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.R;
import com.jens.automation2.Rule;
import com.jens.automation2.Trigger;
import com.jens.automation2.Trigger.Trigger_Enum;
import java.util.ArrayList;
public class PhoneStatusListener implements AutomationListenerInterface
{
protected static int currentStateIncoming = -1;
protected static int currentStateOutgoing = -1;
// protected static int currentStateIncoming = -1;
// protected static int currentStateOutgoing = -1;
protected static String lastPhoneNumber="";
protected static int lastPhoneDirection = -1; //0=incoming, 1=outgoing
protected static int currentState = -1;
protected static boolean incomingCallsReceiverActive = false;
protected static boolean outgoingCallsReceiverActive = false;
@ -31,7 +35,6 @@ public class PhoneStatusListener implements AutomationListenerInterface
protected static IncomingCallsReceiver incomingCallsReceiverInstance;
protected static BroadcastReceiver outgoingCallsReceiverInstance;
public static boolean isIncomingCallsReceiverActive()
{
return incomingCallsReceiverActive;
@ -58,7 +61,17 @@ public class PhoneStatusListener implements AutomationListenerInterface
{
return lastPhoneNumber;
}
public static void setCurrentState(int currentState)
{
PhoneStatusListener.currentState = currentState;
}
public static int getCurrentState()
{
return currentState;
}
public static class IncomingCallsReceiver extends PhoneStateListener
{
@Override
@ -66,174 +79,126 @@ public class PhoneStatusListener implements AutomationListenerInterface
{
// Miscellaneous.logEvent("i", "Call state", "New call state: " + String.valueOf(state), 4);
if(incomingNumber != null && incomingNumber.length() > 0) // check for null in case call comes in with suppressed number.
setLastPhoneNumber(incomingNumber);
switch(state)
/*
Unfortunately receivers for incoming and outgoing calls behave pretty differently:
The Outgoing-Receiver is called when starting a call (ringing)
It is not called when that outgoing call ends however, only the incoming receiver.
If the last call was outgoing the state has not changed to idle this is kind of a fake alert.
*/
if(lastPhoneDirection == 2 && currentState != TelephonyManager.CALL_STATE_IDLE)
{
case TelephonyManager.CALL_STATE_IDLE:
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_IDLE", 4);
if(currentStateIncoming == TelephonyManager.CALL_STATE_OFFHOOK)
setCurrentStateIncoming(state);
else if(currentStateOutgoing == TelephonyManager.CALL_STATE_OFFHOOK)
setCurrentStateOutgoing(state);
else
currentStateIncoming = state;
currentStateOutgoing = state;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_OFFHOOK", 4);
if(currentStateIncoming == TelephonyManager.CALL_STATE_RINGING)
setCurrentStateIncoming(state);
else if(currentStateOutgoing == TelephonyManager.CALL_STATE_RINGING)
setCurrentStateOutgoing(state);
break;
case TelephonyManager.CALL_STATE_RINGING:
String number = "unknown";
if(incomingNumber != null && incomingNumber.length() > 0)
number = incomingNumber;
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.incomingCallFrom), number), 4);
setCurrentStateIncoming(state);
break;
// This status update is actually for an outgoing call
setCurrentState(state);
if(incomingNumber != null && incomingNumber.length() > 0) // check for null in case call comes in with suppressed number.
setLastPhoneNumber(incomingNumber);
switch(state)
{
case TelephonyManager.CALL_STATE_IDLE:
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_IDLE", 4);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_OFFHOOK", 4);
break;
case TelephonyManager.CALL_STATE_RINGING:
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingCallTo), incomingNumber), 4);
break;
}
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionOutgoing);
for(int i=0; i<ruleCandidates.size(); i++)
{
AutomationService asInstance = AutomationService.getInstance();
if(asInstance != null)
if(ruleCandidates.get(i).applies(asInstance))
ruleCandidates.get(i).activate(asInstance, false);
}
}
else
{
// state != TelephonyManager.CALL_STATE_IDLE &&
setCurrentState(state);
setLastPhoneDirection(1);
if (incomingNumber != null && incomingNumber.length() > 0) // check for null in case call comes in with suppressed number.
setLastPhoneNumber(incomingNumber);
switch (state)
{
case TelephonyManager.CALL_STATE_IDLE:
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_IDLE", 4);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_OFFHOOK", 4);
break;
case TelephonyManager.CALL_STATE_RINGING:
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.incomingCallFrom), incomingNumber), 4);
break;
}
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionIncoming);
for (int i = 0; i < ruleCandidates.size(); i++)
{
AutomationService asInstance = AutomationService.getInstance();
if (asInstance != null)
if (ruleCandidates.get(i).applies(asInstance))
ruleCandidates.get(i).activate(asInstance, false);
}
}
}
}
static void setLastPhoneDirection(int i)
{
lastPhoneDirection = i;
}
public static class OutgoingCallsReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
setCurrentStateOutgoing(2);
setLastPhoneNumber(intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingCallFrom), getLastPhoneNumber()), 4);
/*
This receiver is ONLY triggered when outgoing calls ring, not when that call is established or ends.
*/
setLastPhoneDirection(2);
// TelephonyManager tm = (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);
// int newState = tm.getCallState();
// setCurrentState(newState);
setCurrentState(TelephonyManager.CALL_STATE_RINGING);
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
setLastPhoneNumber(phoneNumber);
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingCallTo), getLastPhoneNumber()), 4);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionOutgoing);
for(int i=0; i<ruleCandidates.size(); i++)
{
AutomationService asInstance = AutomationService.getInstance();
if(asInstance != null)
if(ruleCandidates.get(i).applies(asInstance))
ruleCandidates.get(i).activate(asInstance, false);
}
}
}
public static boolean isInACall()
{
if(isInIncomingCall() | isInOutgoingCall())
return true;
return false;
}
public static boolean isInIncomingCall()
{
// Miscellaneous.logEvent("i", "Incoming call state", String.valueOf(currentStateIncoming), 5);
switch(currentStateIncoming)
{
// case -1:
// return false;
// case 0:
// return false;
// case 1:
// return true;
case 2:
return true;
// case 3:
// return true;
// case 4:
// return true;
// default:
// return false;
}
return false;
}
public static boolean isInOutgoingCall()
{
// Miscellaneous.logEvent("i", "Outgoing call state", String.valueOf(currentStateOutgoing), 5);
switch(currentStateOutgoing)
{
// case -1:
// return false;
// case 0:
// return false;
// case 1:
// return true;
case 2:
return true;
// case 3:
// return true;
// case 4:
// return true;
// default:
// return false;
}
return false;
}
private static void setCurrentStateIncoming(int state)
{
// Miscellaneous.logEvent("i", "Call state", "New incoming call state: " + String.valueOf(state), 4);
if(currentStateIncoming != state)
{
if(lastPhoneDirection != 1)
lastPhoneDirection = 1;
if(
(state == 0 && currentStateIncoming == 2)
|
(state == 2 && (currentStateIncoming == 0 | currentStateIncoming == 1))
)
{
currentStateIncoming = state;
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPhoneCall(isInIncomingCall());
for(int i=0; i<ruleCandidates.size(); i++)
{
AutomationService asInstance = AutomationService.getInstance();
if(asInstance != null)
if(ruleCandidates.get(i).applies(asInstance))
ruleCandidates.get(i).activate(asInstance, false);
}
}
else
currentStateIncoming = state;
}
}
public static int getCurrentStateIncoming()
{
return currentStateIncoming;
return getCurrentState() != TelephonyManager.CALL_STATE_IDLE;
}
public static void setCurrentStateOutgoing(int state)
{
if(currentStateOutgoing != state)
{
if(lastPhoneDirection != 2)
lastPhoneDirection = 2;
if(
(state == 0 && currentStateOutgoing == 2)
|
(state == 2 && (currentStateOutgoing == 0 | currentStateOutgoing == 1)))
{
PhoneStatusListener.currentStateOutgoing = state;
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPhoneCall(isInOutgoingCall());
for(int i=0; i<ruleCandidates.size(); i++)
{
AutomationService asInstance = AutomationService.getInstance();
if(asInstance != null)
if(ruleCandidates.get(i).applies(asInstance))
ruleCandidates.get(i).activate(asInstance, false);
}
}
else
PhoneStatusListener.currentStateOutgoing = state;
}
}
public static int getCurrentStateOutgoing()
{
return currentStateOutgoing;
}
/*
Future remark:
Apps that redirect outgoing calls should use the android.telecom.CallRedirectionService API.
Apps that perform call screening should use the android.telecom.CallScreeningService API.
*/
public static void startPhoneStatusListener(AutomationService automationService)
{
@ -312,9 +277,9 @@ public class PhoneStatusListener implements AutomationListenerInterface
public static boolean haveAllPermission()
{
return
ActivityPermissions.havePermission("android.permission.READ_PHONE_STATE", Miscellaneous.getAnyContext())
ActivityPermissions.havePermission(Manifest.permission.READ_PHONE_STATE, Miscellaneous.getAnyContext())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameCall, Miscellaneous.getAnyContext());
ActivityPermissions.havePermission(Manifest.permission.PROCESS_OUTGOING_CALLS, Miscellaneous.getAnyContext());
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2014 Jorrit "Chainfire" Jongma
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,18 +20,24 @@ import android.content.Context;
import android.os.Handler;
import android.widget.Toast;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Base application class to extend from, solving some issues with
* toasts and AsyncTasks you are likely to run into
*/
@SuppressWarnings("WeakerAccess")
public class Application extends android.app.Application {
/**
* Shows a toast message
*
*
* @param context Any context belonging to this application
* @param message The message to show
*/
public static void toast(Context context, String message) {
@AnyThread
public static void toast(@Nullable Context context, @NonNull String message) {
// this is a static method so it is easier to call,
// as the context checking and casting is done for you
@ -45,7 +51,7 @@ public class Application extends android.app.Application {
final Context c = context;
final String m = message;
((Application)context).runInApplicationThread(new Runnable() {
((Application) context).runInApplicationThread(new Runnable() {
@Override
public void run() {
Toast.makeText(c, m, Toast.LENGTH_LONG).show();
@ -54,14 +60,15 @@ public class Application extends android.app.Application {
}
}
private static Handler mApplicationHandler = new Handler();
private static final Handler mApplicationHandler = new Handler();
/**
* Run a runnable in the main application thread
*
*
* @param r Runnable to run
*/
public void runInApplicationThread(Runnable r) {
@AnyThread
public void runInApplicationThread(@NonNull Runnable r) {
mApplicationHandler.post(r);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2014 Jorrit "Chainfire" Jongma
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,12 +18,19 @@ package eu.chainfire.libsuperuser;
import android.os.Looper;
import android.util.Log;
import android.os.Process;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.jens.automation2.BuildConfig;
/**
* Utility class for logging and debug features that (by default) does nothing when not in debug mode
*/
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"})
@AnyThread
public class Debug {
// ----- DEBUGGING -----
@ -32,23 +39,23 @@ public class Debug {
/**
* <p>Enable or disable debug mode</p>
*
*
* <p>By default, debug mode is enabled for development
* builds and disabled for exported APKs - see
* BuildConfig.DEBUG</p>
*
*
* @param enable Enable debug mode ?
*/
public static void setDebug(boolean enable) {
debug = enable;
*/
public static void setDebug(boolean enable) {
debug = enable;
}
/**
* <p>Is debug mode enabled ?</p>
*
*
* @return Debug mode enabled
*/
public static boolean getDebug() {
public static boolean getDebug() {
return debug;
}
@ -63,25 +70,27 @@ public class Debug {
public static final int LOG_GENERAL = 0x0001;
public static final int LOG_COMMAND = 0x0002;
public static final int LOG_OUTPUT = 0x0004;
public static final int LOG_POOL = 0x0008;
public static final int LOG_NONE = 0x0000;
public static final int LOG_ALL = 0xFFFF;
private static int logTypes = LOG_ALL;
@Nullable
private static OnLogListener logListener = null;
/**
* <p>Log a message (internal)</p>
*
* <p>Current debug and enabled logtypes decide what gets logged -
* even if a custom callback is registered</p>
*
* @param type Type of message to log
*
* <p>Current debug and enabled logtypes decide what gets logged -
* even if a custom callback is registered</p>
*
* @param type Type of message to log
* @param typeIndicator String indicator for message type
* @param message The message to log
* @param message The message to log
*/
private static void logCommon(int type, String typeIndicator, String message) {
private static void logCommon(int type, @NonNull String typeIndicator, @NonNull String message) {
if (debug && ((logTypes & type) == type)) {
if (logListener != null) {
logListener.onLog(type, typeIndicator, message);
@ -89,52 +98,61 @@ public class Debug {
Log.d(TAG, "[" + TAG + "][" + typeIndicator + "]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message);
}
}
}
}
/**
* <p>Log a "general" message</p>
*
*
* <p>These messages are infrequent and mostly occur at startup/shutdown or on error</p>
*
*
* @param message The message to log
*/
public static void log(String message) {
public static void log(@NonNull String message) {
logCommon(LOG_GENERAL, "G", message);
}
/**
* <p>Log a "per-command" message</p>
*
*
* <p>This could produce a lot of output if the client runs many commands in the session</p>
*
*
* @param message The message to log
*/
public static void logCommand(String message) {
public static void logCommand(@NonNull String message) {
logCommon(LOG_COMMAND, "C", message);
}
/**
* <p>Log a line of stdout/stderr output</p>
*
*
* <p>This could produce a lot of output if the shell commands are noisy</p>
*
*
* @param message The message to log
*/
public static void logOutput(String message) {
public static void logOutput(@NonNull String message) {
logCommon(LOG_OUTPUT, "O", message);
}
/**
* <p>Log pool event</p>
*
* @param message The message to log
*/
public static void logPool(@NonNull String message) {
logCommon(LOG_POOL, "P", message);
}
/**
* <p>Enable or disable logging specific types of message</p>
*
*
* <p>You may | (or) LOG_* constants together. Note that
* debug mode must also be enabled for actual logging to
* occur.</p>
*
* @param type LOG_* constants
*
* @param type LOG_* constants
* @param enable Enable or disable
*/
public static void setLogTypeEnabled(int type, boolean enable) {
public static void setLogTypeEnabled(int type, boolean enable) {
if (enable) {
logTypes |= type;
} else {
@ -144,26 +162,28 @@ public class Debug {
/**
* <p>Is logging for specific types of messages enabled ?</p>
*
*
* <p>You may | (or) LOG_* constants together, to learn if
* <b>all</b> passed message types are enabled for logging. Note
* that debug mode must also be enabled for actual logging
* to occur.</p>
*
*
* @param type LOG_* constants
* @return enabled?
*/
public static boolean getLogTypeEnabled(int type) {
return ((logTypes & type) == type);
public static boolean getLogTypeEnabled(int type) {
return ((logTypes & type) == type);
}
/**
* <p>Is logging for specific types of messages enabled ?</p>
*
*
* <p>You may | (or) LOG_* constants together, to learn if
* <b>all</b> message types are enabled for logging. Takes
* debug mode into account for the result.</p>
*
*
* @param type LOG_* constants
* @return enabled and in debug mode?
*/
public static boolean getLogTypeEnabledEffective(int type) {
return getDebug() && getLogTypeEnabled(type);
@ -171,22 +191,23 @@ public class Debug {
/**
* <p>Register a custom log handler</p>
*
*
* <p>Replaces the log method (write to logcat) with your own
* handler. Whether your handler gets called is still dependent
* on debug mode and message types being enabled for logging.</p>
*
*
* @param onLogListener Custom log listener or NULL to revert to default
*/
public static void setOnLogListener(OnLogListener onLogListener) {
public static void setOnLogListener(@Nullable OnLogListener onLogListener) {
logListener = onLogListener;
}
/**
* <p>Get the currently registered custom log handler</p>
*
* @return Current custom log handler or NULL if none is present
*
* @return Current custom log handler or NULL if none is present
*/
@Nullable
public static OnLogListener getOnLogListener() {
return logListener;
}
@ -197,10 +218,10 @@ public class Debug {
/**
* <p>Enable or disable sanity checks</p>
*
* <p>Enables or disables the library crashing when su is called
*
* <p>Enables or disables the library crashing when su is called
* from the main thread.</p>
*
*
* @param enable Enable or disable
*/
public static void setSanityChecksEnabled(boolean enable) {
@ -209,10 +230,10 @@ public class Debug {
/**
* <p>Are sanity checks enabled ?</p>
*
*
* <p>Note that debug mode must also be enabled for actual
* sanity checks to occur.</p>
*
* sanity checks to occur.</p>
*
* @return True if enabled
*/
public static boolean getSanityChecksEnabled() {
@ -221,9 +242,9 @@ public class Debug {
/**
* <p>Are sanity checks enabled ?</p>
*
* <p>Takes debug mode into account for the result.</p>
*
*
* <p>Takes debug mode into account for the result.</p>
*
* @return True if enabled
*/
public static boolean getSanityChecksEnabledEffective() {
@ -232,11 +253,11 @@ public class Debug {
/**
* <p>Are we running on the main thread ?</p>
*
*
* @return Running on main thread ?
*/
*/
public static boolean onMainThread() {
return ((Looper.myLooper() != null) && (Looper.myLooper() == Looper.getMainLooper()));
return ((Looper.myLooper() != null) && (Looper.myLooper() == Looper.getMainLooper()) && (Process.myUid() != 0));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2014 Jorrit "Chainfire" Jongma
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,6 +37,7 @@ import android.content.Intent;
* window possibly obscuring SuperSU dialogs".
* </p>
*/
@SuppressWarnings({"unused"})
public abstract class HideOverlaysReceiver extends BroadcastReceiver {
public static final String ACTION_HIDE_OVERLAYS = "eu.chainfire.supersu.action.HIDE_OVERLAYS";
public static final String CATEGORY_HIDE_OVERLAYS = Intent.CATEGORY_INFO;
@ -45,15 +46,17 @@ public abstract class HideOverlaysReceiver extends BroadcastReceiver {
@Override
public final void onReceive(Context context, Intent intent) {
if (intent.hasExtra(EXTRA_HIDE_OVERLAYS)) {
onHideOverlays(intent.getBooleanExtra(EXTRA_HIDE_OVERLAYS, false));
onHideOverlays(context, intent, intent.getBooleanExtra(EXTRA_HIDE_OVERLAYS, false));
}
}
/**
* Called when overlays <em>should</em> be hidden or <em>may</em> be shown
* again.
*
*
* @param context App context
* @param intent Received intent
* @param hide Should overlays be hidden?
*/
public abstract void onHideOverlays(boolean hide);
public abstract void onHideOverlays(Context context, Intent intent, boolean hide);
}

View File

@ -0,0 +1,186 @@
/*
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.chainfire.libsuperuser;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
@SuppressWarnings("WeakerAccess")
@AnyThread
public class MarkerInputStream extends InputStream {
private static final String EXCEPTION_EOF = "EOF encountered, shell probably died";
@NonNull
private final StreamGobbler gobbler;
private final InputStream inputStream;
private final byte[] marker;
private final int markerLength;
private final int markerMaxLength;
private final byte[] read1 = new byte[1];
private final byte[] buffer = new byte[65536];
private int bufferUsed = 0;
private volatile boolean eof = false;
private volatile boolean done = false;
public MarkerInputStream(@NonNull StreamGobbler gobbler, @NonNull String marker) throws UnsupportedEncodingException {
this.gobbler = gobbler;
this.gobbler.suspendGobbling();
this.inputStream = gobbler.getInputStream();
this.marker = marker.getBytes("UTF-8");
this.markerLength = marker.length();
this.markerMaxLength = marker.length() + 5; // marker + space + exitCode(max(3)) + \n
}
@Override
public int read() throws IOException {
while (true) {
int r = read(read1, 0, 1);
if (r < 0) return -1;
if (r == 0) {
// wait for data to become available
try {
Thread.sleep(16);
} catch (InterruptedException e) {
// no action
}
continue;
}
return (int)read1[0] & 0xFF;
}
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return read(b, 0, b.length);
}
private void fill(int safeSizeToWaitFor) {
// fill up our own buffer
if (isEOF()) return;
try {
int a;
while (((a = inputStream.available()) > 0) || (safeSizeToWaitFor > 0)) {
int left = buffer.length - bufferUsed;
if (left == 0) return;
int r = inputStream.read(buffer, bufferUsed, Math.max(safeSizeToWaitFor, Math.min(a, left)));
if (r >= 0) {
bufferUsed += r;
safeSizeToWaitFor -= r;
} else {
// This shouldn't happen *unless* we have both the full content and the end
// marker, otherwise the shell was interrupted/died. An IOException is raised
// in read() below if that is the case.
setEOF();
break;
}
}
} catch (IOException e) {
setEOF();
}
}
@Override
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
if (done) return -1;
fill(markerLength - bufferUsed);
// we need our buffer to be big enough to detect the marker
if (bufferUsed < markerLength) return 0;
// see if we have our marker
int match = -1;
for (int i = Math.max(0, bufferUsed - markerMaxLength); i < bufferUsed - markerLength; i++) {
boolean found = true;
for (int j = 0; j < markerLength; j++) {
if (buffer[i + j] != marker[j]) {
found = false;
break;
}
}
if (found) {
match = i;
break;
}
}
if (match == 0) {
// marker is at the front of the buffer
while (buffer[bufferUsed -1] != (byte)'\n') {
if (isEOF()) throw new IOException(EXCEPTION_EOF);
fill(1);
}
if (gobbler.getOnLineListener() != null) gobbler.getOnLineListener().onLine(new String(buffer, 0, bufferUsed - 1, "UTF-8"));
done = true;
return -1;
} else {
int ret;
if (match == -1) {
if (isEOF()) throw new IOException(EXCEPTION_EOF);
// marker isn't in the buffer, drain as far as possible while keeping some space
// leftover so we can still find the marker if its read is split between two fill()
// calls
ret = Math.min(len, bufferUsed - markerMaxLength);
} else {
// even if eof, it is possibly we have both the content and the end marker, which
// counts as a completed command, so we don't throw IOException here
// marker found, max drain up to marker, this will eventually cause the marker to be
// at the front of the buffer
ret = Math.min(len, match);
}
if (ret > 0) {
System.arraycopy(buffer, 0, b, off, ret);
bufferUsed -= ret;
System.arraycopy(buffer, ret, buffer, 0, bufferUsed);
} else {
try {
// prevent 100% CPU on reading from for example /dev/random
Thread.sleep(4);
} catch (Exception e) {
// no action
}
}
return ret;
}
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public synchronized void close() throws IOException {
if (!isEOF() && !done) {
// drain
byte[] buffer = new byte[1024];
while (read(buffer) >= 0) {
}
}
}
public synchronized boolean isEOF() {
return eof;
}
public synchronized void setEOF() {
eof = true;
}
}

View File

@ -0,0 +1,243 @@
/*
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.chainfire.libsuperuser;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
/**
* Helper class for modifying SELinux policies, reducing the number of calls to a minimum.
*
* Example usage:
*
* <pre>
* <code>
*
* private class Policy extends eu.chainfire.libsuperuser.Policy {
* {@literal @}Override protected String[] getPolicies() {
* return new String[] {
* "allow sdcardd unlabeled dir { append create execute write relabelfrom link unlink ioctl getattr setattr read rename lock mounton quotaon swapon rmdir audit_access remove_name add_name reparent execmod search open }",
* "allow sdcardd unlabeled file { append create write relabelfrom link unlink ioctl getattr setattr read rename lock mounton quotaon swapon audit_access open }",
* "allow unlabeled unlabeled filesystem associate"
* };
* }
* };
* private Policy policy = new Policy();
*
* public void someFunctionNotCalledOnMainThread() {
* policy.inject();
* }
*
* </code>
* </pre>
*/
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"})
public abstract class Policy {
/**
* supolicy should be called as little as possible. We batch policies together. The command
* line is guaranteed to be able to take 4096 characters. Reduce by a bit for supolicy itself.
*/
private static final int MAX_POLICY_LENGTH = 4096 - 32;
private static final Object synchronizer = new Object();
@Nullable
private static volatile Boolean canInject = null;
private static volatile boolean injected = false;
/**
* @return Have we injected our policies already?
*/
@AnyThread
public static boolean haveInjected() {
return injected;
}
/**
* Reset policies-have-been-injected state, if you really need to inject them again. Extremely
* rare, you will probably never need this.
*/
@AnyThread
public static void resetInjected() {
synchronized (synchronizer) {
injected = false;
}
}
/**
* Override this method to return a array of strings containing the policies you want to inject.
*
* @return Policies to inject
*/
@NonNull
protected abstract String[] getPolicies();
/**
* Detects availability of the supolicy tool. Only useful if Shell.SU.isSELinuxEnforcing()
* returns true. Caches return value, can safely be called from the UI thread <b>if</b> a
* a cached value exists.
*
* @see #resetCanInject()
*
* @return canInject?
*/
@SuppressWarnings({"deprecation", "ConstantConditions"})
@WorkerThread // first call only
public static boolean canInject() {
synchronized (synchronizer) {
if (canInject != null) return canInject;
canInject = false;
// We are making the assumption here that if supolicy is called without parameters,
// it will return output (such as a usage notice) on STDOUT (not STDERR) that contains
// at least the word "supolicy". This is true at least for SuperSU.
List<String> result = Shell.run("sh", new String[] { "supolicy" }, null, false);
if (result != null) {
for (String line : result) {
if (line.contains("supolicy")) {
canInject = true;
break;
}
}
}
return canInject;
}
}
/**
* Reset cached can-inject state and force redetection on nect canInject() call
*/
@AnyThread
public static void resetCanInject() {
synchronized (synchronizer) {
canInject = null;
}
}
/**
* Transform the policies defined by getPolicies() into a set of shell commands
*
* @return Possibly empty List of commands, or null
*/
@Nullable
protected List<String> getInjectCommands() {
return getInjectCommands(true);
}
/**
* Transform the policies defined by getPolicies() into a set of shell commands
*
* @param allowBlocking allow method to perform blocking I/O for extra checks
* @return Possibly empty List of commands, or null
*/
@Nullable
@SuppressWarnings("all")
@WorkerThread // if allowBlocking
protected List<String> getInjectCommands(boolean allowBlocking) {
synchronized (synchronizer) {
// No reason to bother if we're in permissive mode
if (!Shell.SU.isSELinuxEnforcing()) return null;
// If we can't inject, no use continuing
if (allowBlocking && !canInject()) return null;
// Been there, done that
if (injected) return null;
// Retrieve policies
String[] policies = getPolicies();
if ((policies != null) && (policies.length > 0)) {
List<String> commands = new ArrayList<String>();
// Combine the policies into a minimal number of commands
String command = "";
for (String policy : policies) {
if ((command.length() == 0) || (command.length() + policy.length() + 3 < MAX_POLICY_LENGTH)) {
command = command + " \"" + policy + "\"";
} else {
commands.add("supolicy --live" + command);
command = "";
}
}
if (command.length() > 0) {
commands.add("supolicy --live" + command);
}
return commands;
}
// No policies
return null;
}
}
/**
* Inject the policies defined by getPolicies(). Throws an exception if called from
* the main thread in debug mode.
*/
@SuppressWarnings({"deprecation"})
@WorkerThread
public void inject() {
synchronized (synchronizer) {
// Get commands that inject our policies
List<String> commands = getInjectCommands();
// Execute them, if any
if ((commands != null) && (commands.size() > 0)) {
Shell.SU.run(commands);
}
// We survived without throwing
injected = true;
}
}
/**
* Inject the policies defined by getPolicies(). Throws an exception if called from
* the main thread in debug mode if waitForIdle is true. If waitForIdle is false
* however, it cannot be guaranteed the command was executed and the policies injected
* upon return.
*
* @param shell Interactive shell to execute commands on
* @param waitForIdle wait for the command to complete before returning?
*/
@WorkerThread // if waitForIdle
public void inject(@NonNull Shell.Interactive shell, boolean waitForIdle) {
synchronized (synchronizer) {
// Get commands that inject our policies
List<String> commands = getInjectCommands(waitForIdle);
// Execute them, if any
if ((commands != null) && (commands.size() > 0)) {
shell.addCommand(commands);
if (waitForIdle) {
shell.waitForIdle();
}
}
// We survived without throwing
injected = true;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2014 Jorrit "Chainfire" Jongma
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,78 +21,139 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Locale;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
/**
* Thread utility class continuously reading from an InputStream
*/
public class StreamGobbler extends Thread {
@SuppressWarnings({"WeakerAccess"})
public class StreamGobbler extends Thread {
private static int threadCounter = 0;
private static int incThreadCounter() {
synchronized (StreamGobbler.class) {
int ret = threadCounter;
threadCounter++;
return ret;
}
}
/**
* Line callback interface
*/
public interface OnLineListener {
public interface OnLineListener {
/**
* <p>Line callback</p>
*
*
* <p>This callback should process the line as quickly as possible.
* Delays in this callback may pause the native process or even
* result in a deadlock</p>
*
*
* @param line String that was gobbled
*/
void onLine(String line);
}
private String shell = null;
private BufferedReader reader = null;
private List<String> writer = null;
private OnLineListener listener = null;
/**
* Stream closed callback interface
*/
public interface OnStreamClosedListener {
/**
* <p>Stream closed callback</p>
*/
void onStreamClosed();
}
@NonNull
private final String shell;
@NonNull
private final InputStream inputStream;
@NonNull
private final BufferedReader reader;
@Nullable
private final List<String> writer;
@Nullable
private final OnLineListener lineListener;
@Nullable
private final OnStreamClosedListener streamClosedListener;
private volatile boolean active = true;
private volatile boolean calledOnClose = false;
/**
* <p>StreamGobbler constructor</p>
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
* possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p>
*
*
* @param shell Name of the shell
* @param inputStream InputStream to read from
* @param outputList List<String> to write to, or null
* @param outputList {@literal List<String>} to write to, or null
*/
public StreamGobbler(String shell, InputStream inputStream, List<String> outputList) {
@AnyThread
public StreamGobbler(@NonNull String shell, @NonNull InputStream inputStream, @Nullable List<String> outputList) {
super("Gobbler#" + incThreadCounter());
this.shell = shell;
this.inputStream = inputStream;
reader = new BufferedReader(new InputStreamReader(inputStream));
writer = outputList;
lineListener = null;
streamClosedListener = null;
}
/**
* <p>StreamGobbler constructor</p>
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
* possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p>
*
*
* @param shell Name of the shell
* @param inputStream InputStream to read from
* @param onLineListener OnLineListener callback
* @param onStreamClosedListener OnStreamClosedListener callback
*/
public StreamGobbler(String shell, InputStream inputStream, OnLineListener onLineListener) {
@AnyThread
public StreamGobbler(@NonNull String shell, @NonNull InputStream inputStream, @Nullable OnLineListener onLineListener, @Nullable OnStreamClosedListener onStreamClosedListener) {
super("Gobbler#" + incThreadCounter());
this.shell = shell;
this.inputStream = inputStream;
reader = new BufferedReader(new InputStreamReader(inputStream));
listener = onLineListener;
lineListener = onLineListener;
streamClosedListener = onStreamClosedListener;
writer = null;
}
@Override
public void run() {
// keep reading the InputStream until it ends (or an error occurs)
// optionally pausing when a command is executed that consumes the InputStream itself
try {
String line;
while ((line = reader.readLine()) != null) {
Debug.logOutput(String.format("[%s] %s", shell, line));
Debug.logOutput(String.format(Locale.ENGLISH, "[%s] %s", shell, line));
if (writer != null) writer.add(line);
if (listener != null) listener.onLine(line);
if (lineListener != null) lineListener.onLine(line);
while (!active) {
synchronized (this) {
try {
this.wait(128);
} catch (InterruptedException e) {
// no action
}
}
}
}
} catch (IOException e) {
// reader probably closed, expected exit condition
if (streamClosedListener != null) {
calledOnClose = true;
streamClosedListener.onStreamClosed();
}
}
// make sure our stream is closed and resources will be freed
@ -101,5 +162,96 @@ public class StreamGobbler extends Thread {
} catch (IOException e) {
// read already closed
}
if (!calledOnClose) {
if (streamClosedListener != null) {
calledOnClose = true;
streamClosedListener.onStreamClosed();
}
}
}
/**
* <p>Resume consuming the input from the stream</p>
*/
@AnyThread
public void resumeGobbling() {
if (!active) {
synchronized (this) {
active = true;
this.notifyAll();
}
}
}
/**
* <p>Suspend gobbling, so other code may read from the InputStream instead</p>
*
* <p>This should <i>only</i> be called from the OnLineListener callback!</p>
*/
@AnyThread
public void suspendGobbling() {
synchronized (this) {
active = false;
this.notifyAll();
}
}
/**
* <p>Wait for gobbling to be suspended</p>
*
* <p>Obviously this cannot be called from the same thread as {@link #suspendGobbling()}</p>
*/
@WorkerThread
public void waitForSuspend() {
synchronized (this) {
while (active) {
try {
this.wait(32);
} catch (InterruptedException e) {
// no action
}
}
}
}
/**
* <p>Is gobbling suspended ?</p>
*
* @return is gobbling suspended?
*/
@AnyThread
public boolean isSuspended() {
synchronized (this) {
return !active;
}
}
/**
* <p>Get current source InputStream</p>
*
* @return source InputStream
*/
@NonNull
@AnyThread
public InputStream getInputStream() {
return inputStream;
}
/**
* <p>Get current OnLineListener</p>
*
* @return OnLineListener
*/
@Nullable
@AnyThread
public OnLineListener getOnLineListener() {
return lineListener;
}
void conditionalJoin() throws InterruptedException {
if (calledOnClose) return; // deadlock from callback, we're inside exit procedure
if (Thread.currentThread() == this) return; // can't join self
join();
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2012-2019 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.chainfire.libsuperuser;
import android.os.Build;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
/**
* Utility class to decide between toolbox and toybox calls on M.
* Note that some calls (such as 'ls') are present in both, this
* class will favor toybox variants.
*
* This may not be what you want, as both syntax and output may
* differ between the variants.
*
* Very specific warning, the 'mount' included with toybox tends
* to segfault, at least on the first few 6.0 firmwares.
*/
@SuppressWarnings({"unused", "WeakerAccess", "deprecation"})
public class Toolbox {
private static final int TOYBOX_SDK = 23;
private static final Object synchronizer = new Object();
@Nullable
private static volatile String toybox = null;
/**
* Initialize. Asks toybox which commands it supports. Throws an exception if called from
* the main thread in debug mode.
*/
@SuppressWarnings("all")
@WorkerThread
public static void init() {
// already inited ?
if (toybox != null) return;
// toybox is M+
if (Build.VERSION.SDK_INT < TOYBOX_SDK) {
toybox = "";
} else {
if (Debug.getSanityChecksEnabledEffective() && Debug.onMainThread()) {
Debug.log(Shell.ShellOnMainThreadException.EXCEPTION_TOOLBOX);
throw new Shell.ShellOnMainThreadException(Shell.ShellOnMainThreadException.EXCEPTION_TOOLBOX);
}
// ask toybox which commands it has, and store the info
synchronized (synchronizer) {
toybox = "";
List<String> output = Shell.SH.run("toybox");
if (output != null) {
toybox = " ";
for (String line : output) {
toybox = toybox + line.trim() + " ";
}
}
}
}
}
/**
* Format a command string, deciding on toolbox or toybox for its execution
*
* If init() has not already been called, it is called for you, which may throw an exception
* if we're in the main thread.
*
* Example:
* Toolbox.command("chmod 0.0 %s", "/some/file/somewhere");
*
* Output:
* &lt; M: "toolbox chmod 0.0 /some/file/somewhere"
* M+ : "toybox chmod 0.0 /some/file/somewhere"
*
* @param format String to format. First word is the applet name.
* @param args Arguments passed to String.format
* @return Formatted String prefixed with either toolbox or toybox
*/
@SuppressWarnings("ConstantConditions")
@WorkerThread // if init() not yet called
public static String command(@NonNull String format, Object... args) {
if (Build.VERSION.SDK_INT < TOYBOX_SDK) {
return String.format(Locale.ENGLISH, "toolbox " + format, args);
}
if (toybox == null) init();
format = format.trim();
String applet;
int p = format.indexOf(' ');
if (p >= 0) {
applet = format.substring(0, p);
} else {
applet = format;
}
if (toybox.contains(" " + applet + " ")) {
return String.format(Locale.ENGLISH, "toybox " + format, args);
} else {
return String.format(Locale.ENGLISH, "toolbox " + format, args);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 4.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

@ -2,17 +2,24 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_margin="10dp" >
android:layout_margin="@dimen/default_margin" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/urlToTriggerExplanation"
android:layout_marginBottom="@dimen/default_margin"/>
<TextView
android:id="@+id/tvRuleTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/urlToTrigger" />
<EditText

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

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

@ -0,0 +1,193 @@
<?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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<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:layout_gravity="center_vertical"
android:text="@string/state" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RadioButton
android:id="@+id/rbTriggerPhoneCallStateRinging"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/ringing" />
<RadioButton
android:id="@+id/rbTriggerPhoneCallStateStarted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/started" />
<RadioButton
android:id="@+id/rbTriggerPhoneCallStateStopped"
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">
<ImageView
android:layout_span="2"
android:layout_width="match_parent"
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_gravity="center_vertical"
android:text="@string/phoneDirection" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RadioButton
android:id="@+id/rbTriggerPhoneCallDirectionAny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/any" />
<RadioButton
android:id="@+id/rbTriggerPhoneCallDirectionIncoming"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/incoming" />
<RadioButton
android:id="@+id/rbTriggerPhoneCallDirectionOutgoing"
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">
<ImageView
android:layout_span="2"
android:layout_width="match_parent"
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_gravity="center_vertical"
android:text="@string/phoneNumber" />
<EditText
android:id="@+id/etTriggerPhoneCallPhoneNumber"
android:inputType="text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
</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:text="@string/phoneNumberExplanation" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_span="2"
android:text="@string/urlRegex" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/bTriggerPhoneCallImportFromContacts"
android:layout_marginVertical="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="2"
android:text="@string/importNumberFromContacts" />
</TableRow>
</TableLayout>
<Button
android:id="@+id/bSaveTriggerPhoneCall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</androidx.appcompat.widget.LinearLayoutCompat>
</ScrollView>

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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="match_parent"
android:layout_height="wrap_content"
android:text="@string/wifiConnection"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:layout_marginBottom="@dimen/default_margin" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1"
android:stretchColumns="1" >
<TableRow>
<TextView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingRight="@dimen/default_margin"
android:text="@string/state"/>
<RadioGroup
android:layout_height="wrap_content"
android:layout_width="match_parent">
<RadioButton
android:id="@+id/rbTriggerWifiConnected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/connected" />
<RadioButton
android:id="@+id/rbTriggerWifiDisconnected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/disconnected" />
</RadioGroup>
</TableRow>
<TableRow>
<TextView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingRight="@dimen/default_margin"
android:text="@string/name"/>
<EditText
android:id="@+id/etTriggerWifiName"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
</TableRow>
<TableRow>
<Button
android:id="@+id/bLoadWifiList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loadWifiList" />
<Spinner
android:id="@+id/spinnerWifiList"
android:enabled="false"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</TableRow>
</TableLayout>
<Button
android:id="@+id/btriggerWifiSave"
android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</androidx.appcompat.widget.LinearLayoutCompat>

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

@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</TabHost>

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

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

@ -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,5 @@
<?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>
@ -16,28 +13,16 @@
<string name="serviceWontStart">Weder Orte noch Regeln sind definitiv. Dienst wird nicht starten.</string>
<string name="serviceStarted">Automations-Dienst gestarted.</string>
<string name="version">Version %1$s.</string>
<string name="logServiceStarting">Dienst wird gestartet.</string>
<string name="logNotAllMeasurings">Es stehen noch nicht alle Messwerte zur Verfügung. Es kann noch kein Vergleich vorgenommen werden.</string>
<string name="distanceBetween">Der Abstand zwischen GPS- und Mobilfunk-Position beträgt</string>
<string name="radiusSuggestion">Meter. Dies +1 sollte der minimale Radius sein.</string>
<string name="distanceBetween">Der Abstand zwischen GPS- und Mobilfunk-Position beträgt %1$d m. Dies +1 sollte der minimale Radius sein.</string>
<string name="comparing">Sowohl Netzwerk- als auch GPS Position sind bekannt. Vergleiche...</string>
<string name="logNoSuitableProvider">Kein brauchbarer Positionsanbieter verfügbar.</string>
<string name="positioningWindowNotice">Falls Sie in einem Gebäude sind wird empfohlen das Gerät in die Nähe eines Fensters zu bringen bis eine Position ermittelt werden konnte. Andernfalls kann es sehr lange dauern oder es funktioniert gar nicht.</string>
<string name="gettingPosition">Position wird ermittelt. Bitte warten...</string>
<string name="logGettingPositionWithProvider">Position wird ermittelt mit Anbieter:</string>
<string name="yes">Ja</string>
<string name="no">Nein</string>
<string name="logGotGpsUpdate">GPS Position erhalten. Genauigkeit:</string>
<string name="logGotNetworkUpdate">Netzwerk Position erhalten. Genauigkeit:</string>
<string name="pleaseEnterValidLatitude">Bitte geben Sie einen gültigen Breitengrad an.</string>
<string name="pleaseEnterValidLongitude">Bitte geben Sie einen gültigen Längengrad an.</string>
<string name="pleaseEnterValidRadius">Bitte geben Sie einen gültigen positiven Radius an.</string>
<string name="selectOneDay">Bitte wählen Sie wenigstens einen Tag aus.</string>
<string name="logAttemptingToBindToService">Versuche zum Dienst zu verbinden... </string>
<string name="logAttemptingToUnbindFromService">Versuche vom Dienst zu trennen... </string>
<string name="logBoundToService">Zum Dienst verbunden.</string>
<string name="logUnboundFromService">Vom Dienst getrennt.</string>
<string name="logServiceAlreadyRunning">Befehl zum Starten des Dienstes, aber er läuft bereits.</string>
<string name="whatToDoWithRule">Was soll mit der Regel gemacht werden?</string>
<string name="whatToDoWithPoi">Was soll mit dem Ort gemacht werden?</string>
<string name="whatToDoWithProfile">Was soll mit dem Profil gemacht werden?</string>
@ -73,7 +58,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 +83,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>
@ -123,14 +108,14 @@
<string name="gpsAccuracy">GPS Genauigkeit [m]</string>
<string name="satisfactoryAccuracyNetwork">Genauigkeit für Mobilfunk Änderungen in Metern</string>
<string name="networkAccuracy">Mobilfunk Genauigkeit [m]</string>
<string name="minimumTimeForLocationUpdates">Minimale Zeitänderung in Sekunden für Positionsupdates</string>
<string name="minimumTimeForLocationUpdates">Minimale Zeitänderung in Millisekunden für Positionsupdates</string>
<string name="timeForUpdate">Zeit für Updates [millisek]</string>
<string name="soundSettings">Ton Einstellungen</string>
<string name="showHelp">Hilfe</string>
<string name="rules">Regeln</string>
<string name="helpTextRules">Alle Auslöser sind UND-verknüpft. D.h. die Regel wird nur zutreffen, wenn alle Bedingungen erfüllt sind. Wenn Sie eine ODER-Verknüpfung möchten, müssen Sie eine weitere Regel erstellen.</string>
<string name="timeframes">Zeiträume</string>
<string name="helpTextTimeFrame">Wenn Sie eine Regel mit einem oder mehreren Zeiträumen erstellen, haben Sie zwei Möglichkeiten. Sie können wählen, ob der Auslöser besagt, daß der Zeitraum entweder verlassen ODER betreten wird. In jedem Fall wird die Regel nur einmal ausgelöst.\nWenn eine Regel z.B. besagt \"betrete timeframe xyz\" und das Klingeltonprofil in Vibration ändert, bedeutet das NICHT, daß das Gerät hinterher automatisch wieder zum normalen Klingelprofil zurückschaltet. Wenn das erwünscht ist, muß eine weitere Regel mit einem Folgezeitraum erstellen werden.</string>
<string name="helpTextTimeFrame">Wenn Sie eine Regel mit einem Zeitraum erstellen, haben Sie zwei Möglichkeiten. Sie können wählen, ob der Auslöser besagt, daß der Zeitraum entweder verlassen ODER betreten wird. In jedem Fall wird die Regel nur einmal ausgelöst. Wenn eine Regel z.B. besagt \"betrete timeframe xyz\" und das Klingeltonprofil in Vibration ändert, bedeutet das NICHT, daß das Gerät hinterher automatisch wieder zum normalen Klingelprofil zurückschaltet. Wenn das erwünscht ist, muß eine weitere Regel mit einem Folgezeitraum erstellen werden.</string>
<string name="helpTextSound">Auf dem Hauptbildschirm können Sie die Funktion Tonänderunugen sperren benutzen, um vorrübergehend regelbasierte Tonänderungen zu deaktivieren. Z.B. könnten Sie in einer Situation oder an einem Ort sein, wo Klingeltöne normalerweise in Ordnung sind, aber dieses eine Mal würde es stören. Die Funktion wird automatisch wieder deaktiviert nachdem die eingestellte Zeit abgelaufen ist. Klicken Sie den + Knopf, um die angezeigte Zeit zur Frist hinzuzufügen. Sobald es aktiv ist, können Sie es mit dem Schalter rechts wieder abschalten (und so regelbasierte Tonänderungen wieder ermöglichen).</string>
<string name="toggableRules">Umkehrbare Regeln</string>
<string name="helpTextToggable">Regeln haben eine Einstellung namens \"Umschaltbar\". Das bedeutet: Wurde eine Regel ausgeführt und anschließend treten dieselben Auslöser wieder ein, wird die Regel nochmal in umgekehrter Weise ausgeführt, wo es möglich ist. Gegenwärtig funktioniert das nur im Zusammenspiel mit NFC Tags. Wenn Sie einen Tag zwei Mal berühren und es eine umschaltbare Regel zu diesem Tag gibt, wird das Programm das Gegenteil von gegenwärtigen Zustand tun, z.B. WLAN ausschalten, wenn es gegenwärtig eingeschaltet ist.</string>
@ -148,7 +133,7 @@
<string name="lengthOfNoiseLevelMeasurementsTitle">Länge einer Lautstärkemessung in Sekunden</string>
<string name="referenceValueForNoiseLevelMeasurementsSummary">Physikalischer Referenzwert für Lautstärkemessungen</string>
<string name="referenceValueForNoiseLevelMeasurementsTitle">Referenz für Lautstärkemessungen</string>
<string name="logLevelSummary">Protokollierungsgrad (1=minimum, 5=maximum)</string>
<string name="logLevelSummary">Protokollierungsgrad (1=Minimum, 5=Maximum)</string>
<string name="logLevelTitle">Protokollierungsgrad</string>
<string name="ruleActive">Regel aktiv</string>
<string name="triggerPointOfInterest">Ort</string>
@ -191,7 +176,6 @@
<string name="general">Allgemein</string>
<string name="generalText">Um dieses Programm zu benutzen, müssen Sie Regeln anlegen. Diese enthalten Auslöser (z.B. ob Sie ein bestimmtes Gebiet betreten oder eine bestimmte Zeitspanne beginnt). Nachdem Sie dies erledigt haben, klicken Sie auf den Ein/Aus Schalter auf dem Hauptbildschirm.</string>
<string name="unknownActionSpecified">Unbekannte Aktion definiert.</string>
<string name="errorTriggeringUrl">Fehler beim Aufrufen der URL</string>
<string name="errorChangingScreenRotation">Fehler beim Ändern der Bildschirmrotation</string>
<string name="errorDeterminingWifiApState">Fehler bei der Statusprüfung des WLAN Routers</string>
<string name="errorActivatingWifiAp">Fehler beim Aktivieren des WLAN Routers</string>
@ -212,16 +196,6 @@
<string name="wifiName">WLAN name</string>
<string name="enterWifiName">Geben Sie den Namen des WLANs ein (SSID). Leer lassen für irgendein WLAN.</string>
<string name="cancel">Abbrechen</string>
<string name="ruleDoesntApplyWeAreSlowerThan">Regel trifft nicht zu. Wir sind langsamer als</string>
<string name="ruleDoesntApplyWeAreFasterThan">Regel trifft nicht zu. Wir sind schneller als</string>
<string name="ruleDoesntApplyItsQuieterThan">Regel trifft nicht zu. Es ist ruhiger als</string>
<string name="ruleDoesntApplyItsLouderThan">Regel trifft nicht zu. Es ist lauter als</string>
<string name="ruleDoesntApplyBatteryLowerThan">Regel trifft nicht zu. Akkustand ist niedriger als</string>
<string name="ruleDoesntApplyBatteryHigherThan">Regel trifft nicht zu. Akkustand ist höher als</string>
<string name="ruleDoesntApplyNotTheCorrectSsid">Regel trifft nicht zu. Nicht die korrekte SSID (gefordert: \"%1$s\", gegeben: \"%2$s\").</string>
<string name="ruleDoesntApplyNoTagLabel">Regel trifft nicht zu. Keine Tag Bezeichnung oder kein Tag.</string>
<string name="ruleDoesntApplyWrongTagLabel">Regel trifft nicht zu. Falsche Tag Bezeichnung.</string>
<string name="ruleIsDeactivatedCantApply">Regel %1$s ist deaktiviert, kann nicht zutreffen.</string>
<string name="starting">starte</string>
<string name="stopping">stoppe</string>
<string name="connecting">verbinde</string>
@ -244,12 +218,8 @@
<string name="runManually">Manuell ausführen</string>
<string name="serviceHasToRunForThat">Der Dienst muß dafür laufen.</string>
<string name="gpsComparison">GPS Vergleich</string>
<string name="gpsComparisonTimeoutStop">Stoppe GPS Vergleichsmessung, Zeitlimit überschritten.</string>
<string name="timeoutForGpsComparisonsTitle">GPS Zeitlimit [sek]</string>
<string name="timeoutForGpsComparisonsSummary">Zeitlimit in Sekunden wie lange versucht wird eine GPS Vergleichsposition zu finden. Danach wird die letzte bekannte Position übernommen.</string>
<string name="startingGpsTimeout">Starting GPS timeout.</string>
<string name="forcedLocationUpdate">Erzwungenes Positionsupdate</string>
<string name="forcedLocationUpdateLong">Da Zeitlimit für Vergleichsmessung überschritten wird jetzt die letzte bekannte Position übernommen.</string>
<string name="rememberLastActivePoiSummary">Wenn Sie an einem Ort sind, Ihr Gerät oder die Anwendung neustarten und den Ort verlassen wird die Anwendung beim nächsten Start Regeln ausführen, die für das Verlassen des Orts definiert sind.</string>
<string name="rememberLastActivePoiTitle">Merke den zuletzt aktiven Ort</string>
<string name="muteTextToSpeechDuringCallsTitle">Während Telefonaten stumm</string>
@ -257,7 +227,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>
@ -265,35 +235,14 @@
<string name="settingsCategoryProcessMonitoring">Prozess Überwachung</string>
<string name="timeBetweenProcessMonitoringsTitle">Sekunden zwischen Prozess-Überwachungen</string>
<string name="timeBetweenProcessMonitoringsSummary">Je niedriger desto höher der Akkuverbrauch</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">Prozessüberwachung</string>
<string name="periodicProcessMonitoringIsAlreadyRunning">Periodische Prozessüberwachung läuft bereits. Kann sie nicht nochmal starten.</string>
<string name="stoppingPeriodicProcessMonitoringEngine">Stoppe Prozessüberwachungs-Dienst.</string>
<string name="periodicProcessMonitoringIsNotActive">Prozessüberwachung läuft nicht. Kann sie nicht stoppen.</string>
<string name="periodicProcessMonitoringStarted">Prozessüberwachung gestarted.</string>
<string name="periodicProcessMonitoringStopped">Prozessüberwachung gestopped.</string>
<string name="rearmingProcessMonitoringMessage">Prozessüberwachung wird erneut scharf geschaltet.</string>
<string name="notRearmingProcessMonitoringMessageStopRequested">Schalte Prozessüberwachung nicht erneut scharf, Stop angefragt.</string>
<string name="messageReceivedStatingProcessMonitoringIsComplete">Nachricht erhalten, die besagt, daß Prozessüberwachung fertig ist.</string>
<string name="appStarted">App gestarted.</string>
<string name="appStopped">App gestopped.</string>
<string name="runningApp">Laufende app</string>
<string name="errorWritingSettingsToPersistentMemory">Fehler beim Speichern der Einstellungen.</string>
<string name="settings">Einstellungen</string>
<string name="writingSettingsToPersistentMemory">Einstellungen werden gespeichert.</string>
<string name="refreshingSettingsFromFileToMemory">Einstellungen werden neu geladen.</string>
<string name="errorReadingSettings">Fehler beim Lesen der Einstellungen.</string>
<string name="invalidStuffStoredInSettingsErasing">Ungültige Daten in den Einstellungen gefunden. Einstellungen werden gelöscht.</string>
<string name="initializingSettingsToPersistentMemory">Einstellungen werden in nicht flüchtigen Speicher initialisiert.</string>
<string name="errorInitializingSettingsToPersistentMemory">Fehler beim Initialisieren der Einstellungen.</string>
<string name="settingsErased">Einstellungen gelöscht.</string>
<string name="settingsSetToDefault">Einstellungen auf Standardwerte gesetzt.</string>
<string name="batteryLevel">Akkustand</string>
<string name="selectSpeed">Geschwindigkeit wählen</string>
<string name="selectBattery">Akkustand wählen</string>
<string name="applyingSettingsAndRules">Aktualisiere Einstellungen, Regeln und Orte.</string>
<string name="privacy">Datenschutzerklärung</string>
<string name="privacyConfirmationText">Ein Browser wird nun geöffnet und die Datenschutzerklärung von der Webseite des Entwicklers laden.</string>
<string name="waitBeforeNextAction">Vor der nächsten Aktion warten</string>
@ -305,9 +254,6 @@
<string name="moveDown">Herunterschieben</string>
<string name="cantMoveUp">Das Objekt kann nicht weiter hochgeschoben werden.</string>
<string name="cantMoveDown">Das Objekt kann nicht weiter heruntergeschoben werden.</string>
<string name="wifiNameSpecifiedCheckingThat">WLAN SSID angegeben, überprüfe...</string>
<string name="wifiNameMatchesRuleWillApply">WLAN SSID stimmt überein. Regel trifft zu.</string>
<string name="noWifiNameSpecifiedAnyWillDo">Keine WLAN SSID angegeben. Jedes WLAN trifft zu.</string>
<string name="ruleCheckOf">RuleCheck von %1$s</string>
<string name="airplaneMode">Flugmodus</string>
<string name="activate">Aktivieren</string>
@ -366,7 +312,6 @@
<string name="nfcNoNdefIntentBut">Kein NFC NDEF intent, sondern</string>
<string name="nfcNotSupportedInThisAndroidVersionYet">NFC wird in dieser Android Version noch nicht unterstützt.</string>
<string name="cantRunRule">Regeln können nicht ausgeführt werden.</string>
<string name="cantDownloadTooFewRequestsInSettings">Kann nichts runterladen. Menge an erlaubten Versuchen ist in den Einstellungen auf weniger als 1 gesetzt.</string>
<string name="nfcApplyTagToRule">Aktuellen Tag in Regel übernehmen</string>
<string name="nfcTagReadSuccessfully">Tag erfolgreich gelesen.</string>
<string name="nfcValueNotSuitable">Gepspeicherter Wert nicht geeignet.</string>
@ -403,8 +348,6 @@
<string name="detectedActivityWalking">Gehen</string>
<string name="detectedActivityRunning">Laufen</string>
<string name="detectedActivityInvalidStatus">Ungültige Tätigkeit</string>
<string name="ruleDoesntApplyActivityGivenButTooLowProbability">Regel trifft nicht zu. Erkannte Tätigkeit %1$s gegebenem, aber mit zu niedriger Wahrscheinlichkeit (%2$s %%), gefordert %3$s %%.</string>
<string name="ruleDoesntApplyActivityNotPresent">Regel trifft nicht zu. Geforderte Tätigkeit %1$s passiert gerade nicht.</string>
<string name="selectTypeOfActivity">Art der Tätigkeit auswählen</string>
<string name="triggerOnlyAvailableIfPlayServicesInstalled">Dieser Auslöser kann nur verwendet werden, wenn die Google Play Dienste installiert sind.</string>
<string name="activityDetectionFrequencyTitle">Frequenz für Aktivitätserkennung [sek]</string>
@ -412,7 +355,7 @@
<string name="activityDetectionRequiredProbabilityTitle">Wahrscheinlichkeit für Aktivitätserkennungen</string>
<string name="activityDetectionRequiredProbabilitySummary">Wahrscheinlichkeit, ab der eine Aktivität als gegeben gilt.</string>
<string name="incomingCallFrom">Eingehender Telefonanruf von %1$s.</string>
<string name="outgoingCallFrom">Ausgehender Telefonanruf von %1$s.</string>
<string name="outgoingCallTo">Ausgehender Telefonanruf von %1$s.</string>
<string name="actionSpeakText">Text sprechen</string>
<string name="textToSpeak">Zu sprechender Text</string>
<string name="toggleNotAllowed">Die Umschaltfunktion ist momentan nur für Regeln erlaubt, die NFC Tags als Auslöser haben. Für weitere Informationen lesen Sie die Hilfe.</string>
@ -444,28 +387,17 @@
<string name="headphoneMicrophone">Mikrofon</string>
<string name="headphoneAny">Egal</string>
<string name="headphoneSelectType">Kopfhörer Typ auswählen</string>
<string name="ruleDoesntApplyWrongHeadphoneType">Regel trifft nicht zu. Falscher Kopfhörertyp.</string>
<string name="ignoringActivityDetectionUpdateTooSoon">Ignoriere Aktivitätserkennungsupdate. Kam früher rein als %1$s Sekunden.</string>
<string name="whatsThis">Was ist das?</string>
<string name="atLeastRuleXisUsingY">Mindestens Regel \"%1$s\" nutzt einen Auslöser vom Typ \"%2$s\".</string>
<string name="privacyLocationingTitle">Private Ortung verwenden</string>
<string name="whatsThis">Was ist das?</string>
<string name="privacyLocationingTitle">Private Ortung verwenden</string>
<string name="privacyLocationingSummary">Ortungsmethoden vermeiden, die Ihre Position dazu an einen Anbieter übermitteln, z.B. Google. Dies wird nur GPS verwenden und daher langsam sein oder nicht ausreichend zuverlässig funktionieren.</string>
<string name="enforcingGps">Private Ortung aktiviert, erzwinge GPS Verwendung.</string>
<string name="notEnforcingGps">Private Ortung nicht aktiviert, verwende reguläre Anbieterauswahl.</string>
<string name="gpsMeasurement">GPS Messung</string>
<string name="gpsMeasurementTimeout">GPS Messung aufgrund Timeout gestoppt.</string>
<string name="cellMastChanged">Mobilfunkmast geändert: %1$s</string>
<string name="noiseDetectionHint">Wenn Sie denken die Lautstärkeerkennung arbeitet nicht korrekt (abhängig von dem Wert, den Sie angeben), bedenken Sie bitte, daß jedes Telefon unterschiedlich ist. In den Einstellungen können Sie daher die \"Referenz für Lautstärkemessungen\" ändern. Für weitere Informationen siehe http://de.wikipedia.org/wiki/Schalldruckpegel. Sie können den Lautstärkentester vom Hauptbildschirm aus aufrufen, um Ihr Gerät zu kalibrieren.</string>
<string name="noiseDetectionHint">Wenn Sie denken die Lautstärkeerkennung arbeitet nicht korrekt (abhängig von dem Wert, den Sie angeben), bedenken Sie bitte, daß jedes Telefon unterschiedlich ist. In den Einstellungen können Sie daher die \"Referenz für Lautstärkemessungen\" ändern. Für weitere Informationen siehe http://de.wikipedia.org/wiki/Schalldruckpegel. Sie können den Lautstärkentester vom Hauptbildschirm aus aufrufen, um Ihr Gerät zu kalibrieren.</string>
<string name="hint">Hinweis</string>
<string name="selectNoiseLevel">Lautstärkepegel auswählen</string>
<string name="poiHasWifiStoppingCellLocationListener">Ort hat WLAN. Stoppe CellLocationListener.</string>
<string name="poiHasNoWifiNotStoppingCellLocationListener">Ort hat kein WLAN. Stoppe CellLocationListener nicht.</string>
<string name="showOnMap">Auf Karte zeigen</string>
<string name="showOnMap">Auf Karte zeigen</string>
<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>
@ -506,10 +438,6 @@
<string name="volumeTest">Lautstärkentest</string>
<string name="volumeTesterExplanation">Um einen dB Wert für die Lautstärkemessung zu berechnen müssen Sie einen sogenannten physikalischen Referenzwert angeben. Bitte lesen Sie bei Wikipedia nach, um mehr zu erfahren. Dieser Wert wird höchstwahrscheinlich für jedes Smartphone oder Tablet anders sein, deshalb diese Testanwendung. Verschieben Sie den Regler, um den gegenwärtig definierten Wert zu ändern. Je höher der Referenzwert desto niedriger wird der dB Wert. Es werden alle paar %1$s Sekunden neue Messungen vorgenommen und das Ergebnis unten angezeigt. Drücken Sie den zurück-Button, wenn Sie einen passenden Wert gefunden haben.</string>
<string name="settingsWillTakeTime">Manche Einstellungen können nicht übernommen werden bevor der Dienst neu gestartet wird.</string>
<string name="phoneIsRooted">Das Gerät ist gerootet.</string>
<string name="phoneIsNotRooted">Das Gerät ist nicht gerootet.</string>
<string name="dataConWithRootSuccess">Die Datenverbindung wurde mit superUser Rechten erfolgreich geändert.</string>
<string name="dataConWithRootFail">Die Datenverbindung konnte mit superUser Rechten nicht geändert werden.</string>
<string name="rootExplanation">Sie müssen Ihr Telefon rooten, damit diese Funktion funktionieren kann. Danach müssen Sie "Regel manuell ausführen", um den SuperUser Berechtigungsdialog zu zeigen. Wenn dieser erscheint, müssen Sie den Haken setzen, der es immer erlaubt. Ansonsten kann die Regel nicht funktionieren, wenn Sie das Telefon gerade nicht benutzen und demnach den nächsten Dialog nicht genehmigen können.</string>
<string name="errorWritingConfig">Fehler beim Schreiben der Konfiguration. Gibt es einen beschreibbaren Speicher, und wurde alle Berechtigungen gegeben?</string>
<string name="phoneNrReplacementError">Die letzte Telefonnummer konnte nicht in die Variable integriert werden. Sie liegt mir nicht vor.</string>
@ -526,7 +454,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 +522,86 @@
<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 im Hintergrund bestimmt werden. Klicken Sie hier für mehr Infos.</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="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. Außerdem können Sie reguläre Audrücke verwenden. Zum Testen selbiger mag ich die Seite:</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>
<string name="warning">Warnung</string>
<string name="ringing">klingelt</string>
<string name="from">von</string>
<string name="to">an</string>
<string name="matching">übereinstimmend</string>
<string name="loadWifiList">WLAN Liste laden</string>
<string name="noKnownWifis">Es gibt keine bekannten WLANs auf Ihrem Gerät.</string>
<string name="needLocationPermForWifiList">Die Liste von WLANs auf Ihrem Gerät könnte verwendet werden, um zu ermitteln, an welchen Orten Sie waren. Deshalb ist die Positions-Berechtigung nötig, um die Liste dieser WLANs zu laden. Wenn Sie eines aus der Liste auswählen möchten, müssen Sie diese Berechtigung gewähren. Wenn nicht, können Sie immer noch eines manuell eingeben.</string>
<string name="urlToTriggerExplanation">Diese Funktion öffnet NICHT den Browser, sondern löst die HTTP Anfrage im Hintergrund aus. Sie können das z.B. benutzen, um Befehle an Ihre Heimautomatisierung zu schicken.</string>
</resources>

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:ns1="urn:oasis:names:tc:xliff:document:1.2">
<string name="accelerometer">Accelerometro</string>
<string name="ConfigurationExportError">There was an error while exporting the configuration.</string>
<string name="accelerometer">Accelerometro</string>
<string name="accelerometerThreshold">Soglia accelerometro</string>
<string name="accelerometerThresholdDescription">Soglia per i movimenti dell\'accelerometro</string>
<string name="accelerometerTimer">Usa l\'accelerometro dopo x minuti senza lunghi cambiamento della cella radiomobile</string>
<string name="accelerometerTimer">Usa l\'accelerometro dopo x minuti senza lunghi cambiamenti delripetitore</string>
<string name="actionChangeSoundProfile">Cambia il profilo sonoro</string>
<string name="actionDataConnection">Connessione di rete</string>
<string name="actionDataConnection">Connessione dati mobile</string>
<string name="actionDisableScreenRotation">disabilita la rotazione dello schermo</string>
<string name="actionEnableScreenRotation">abilita la rotazione dello schermo</string>
<string name="actionPlayMusic">Apri il lettore musicale</string>
@ -16,42 +17,50 @@
<string name="actionSetUsbTethering">Tethering USB</string>
<string name="actionSetWifi">Wifi</string>
<string name="actionSetWifiTethering">Wifi Tethering</string>
<string name="actionSpeakText">Pronuncia</string>
<string name="actionTriggerUrl">Apri URL</string>
<string name="actionTurnAirplaneModeOff">termina la modalità aeroplano</string>
<string name="actionTurnAirplaneModeOn">passa a modalità aeroplano</string>
<string name="actionSpeakText">Pronuncia testo</string>
<string name="actionTriggerUrl">Apri indirizzo</string>
<string name="actionTurnAirplaneModeOff">termina la modalità aereo</string>
<string name="actionTurnAirplaneModeOn">passa a modalità aereo</string>
<string name="actionTurnBluetoothOff">attiva Bluetooth</string>
<string name="actionTurnBluetoothOn">disattiva il Bluetooth</string>
<string name="actionTurnBluetoothOn">disattiva Bluetooth</string>
<string name="actionTurnUsbTetheringOff">disattiva Tethering USB</string>
<string name="actionTurnUsbTetheringOn">attiva Tethering USB</string>
<string name="actionTurnWifiOff">disattiva Wifi</string>
<string name="actionTurnWifiOn">attiva Wifi</string>
<string name="actionTurnWifiTetheringOff">disattiva Tethering Wifi</string>
<string name="actionTurnWifiTetheringOn">attiva Tethering Wifi</string>
<string name="actions">Azioni(e)</string>
<string name="actions">Azione(i)</string>
<string name="actionsComment">(saranno eseguite in quest\'ordine)</string>
<string name="activate">Attivazione</string>
<string name="activate">Attiva</string>
<string name="activated">attivo</string>
<string name="activating">Sto attivando</string>
<string name="activePoi">Posizione attiva:</string>
<string name="activityDetection">Rilevamento di attività</string>
<string name="activityDetectionFrequencySummary">Secondi tra i tentativi di rilevazione dell\'attività.</string>
<string name="activityDetectionFrequencyTitle">Frequenza di rilevazione [sec]</string>
<string name="activityDetectionRequiredProbabilitySummary">L\'attendibilità dellesecuzione delle attività.</string>
<string name="activityDetectionRequiredProbabilityTitle">Rilevazione di probabile attività</string>
<string name="activityDetection">Rilevamento dell\'attività</string>
<string name="activityDetectionFrequencySummary">Secondi tra i tentativi di rilevamento dell\'attività.</string>
<string name="activityDetectionFrequencyTitle">Frequenza di rilevamento [sec]</string>
<string name="activityDetectionRequiredProbabilitySummary">Attendibilità alla quale esecuzione delle attività è considerata corretta.</string>
<string name="activityDetectionRequiredProbabilityTitle">Probabilità di rilevamento dell\'attività</string>
<string name="activityOrActionName">Nome attività/azione</string>
<string name="addAction">Aggiungi azione</string>
<string name="addIntentValue">Aggiungi una coppia di Intent</string>
<string name="addIntentValue">Aggiungi una coppia di intenzioni</string>
<string name="addParameters">Aggiungi parametri</string>
<string name="addPoi">Aggiungi posizione</string>
<string name="addProfile">Aggiungi profilo</string>
<string name="addRule">Aggiugni regola</string>
<string name="addTrigger">Aggiungi evento</string>
<string name="airplaneMode">Modalità aeroplano</string>
<string name="airplaneModeSdk17Warning">A partire dalla versione Android 4.2 questa funzione è disponibile solo se il dispositivo è rootato.</string>
<string name="addTrigger">Aggiungi evento di attivazione</string>
<string name="airplaneMode">Modalità aereo</string>
<string name="airplaneModeSdk17Warning">A partire dalla versione Android 4.2 questa funzione è disponibile solo se il dispositivo ha accesso root.</string>
<string name="alwaysPlay">riproduci sempre</string>
<string name="alwaysPlayExplanation">Se questa impostazione è attiva, il suono sarà sempre riprodotto. Se questa è disattivata, la riproduzione avverrà quando il telefono non è nè in mute, nè in vibrazione. Comunque, quando attiva, non avrà effetto sul volume. Quindi, per esempio, se la il telefono ha la suoneria attivata, questa opzione non aumenterà il volume di riproduzione multimediale. E di conseguenza, se il volume del suono di riproduzione multimediale è in mute, non potrai ascoltare nulla.</string>
<string name="android.permission.ACCESS_BACKGROUND_LOCATION">Leggi posizione in secondo piano.</string>
<string name="android.permission.ACCESS_COARSE_LOCATION">Leggi posizione approssimativa</string>
<string name="android.permission.ACCESS_FINE_LOCATION">Leggi posizione precisa</string>
<string name="android.permission.ACCESS_NETWORK_STATE">Leggi lo stato della rete mobile</string>
<string name="android.permission.ACCESS_NOTIFICATION_POLICY">Ignore la politica di "non disturbare"</string>
<string name="android.permission.ACCESS_WIFI_STATE">Leggi lo stato della rete wifi</string>
<string name="android.permission.ACTIVITY_RECOGNITION">Identificazione attività</string>
<string name="android.permission.BATTERY_STATS">Leggere lo stato della batteria</string>
<string name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">Leggere le notifiche di sistema</string>
<string name="android.permission.BLUETOOTH">Cambia le impostazioni Bluetooth</string>
<string name="android.permission.BLUETOOTH_ADMIN">Cambia le impostazioni Bluetooth</string>
<string name="android.permission.CHANGE_BACKGROUND_DATA_SETTING">Cambia la connessione dati</string>
@ -60,184 +69,200 @@
<string name="android.permission.MODIFY_AUDIO_SETTINGS">Cambia le impostazioni audio</string>
<string name="android.permission.MODIFY_PHONE_STATE">Cambia le impostazioni del dispositivo</string>
<string name="android.permission.NFC">Usa il modulo NFC</string>
<string name="android.permission.PROCESS_OUTGOING_CALLS">Rileva chiamata in uscita</string>
<string name="android.permission.READ_CALENDAR">Legge gli appuntamenti dal calendario</string>
<string name="android.permission.READ_CALL_LOG">Legge il registro chiamate</string>
<string name="android.permission.PROCESS_OUTGOING_CALLS">Rileva chiamate in uscita</string>
<string name="android.permission.READ_CALENDAR">Leggere gli appuntamenti dal calendario</string>
<string name="android.permission.READ_CALL_LOG">Leggere il registro chiamate</string>
<string name="android.permission.READ_CONTACTS">Leggere informazioni dai Contatti</string>
<string name="android.permission.READ_EXTERNAL_STORAGE">Legge la memoria esterna</string>
<string name="android.permission.READ_PHONE_STATE">Rileva lo stato del telefono</string>
<string name="android.permission.RECEIVE_BOOT_COMPLETED">Rileva che il dispositivo è stato riavviato</string>
<string name="android.permission.RECORD_AUDIO">Registra l\'audio</string>
<string name="android.permission.VIBRATE">Fa vibrare il telefono</string>
<string name="android.permission.RECEIVE_BOOT_COMPLETED">Rileva riavvio del dispositivo</string>
<string name="android.permission.RECORD_AUDIO">Registra audio</string>
<string name="android.permission.SEND_SMS">Invia messaggio di testo</string>
<string name="android.permission.VIBRATE">Attiva vibrazione</string>
<string name="android.permission.WAKE_LOCK">Mantiene attivo il dispositivo</string>
<string name="android.permission.WRITE_EXTERNAL_STORAGE">Scrive su SD</string>
<string name="android.permission.WRITE_EXTERNAL_STORAGE">Scrive sulla SD</string>
<string name="android.permission.WRITE_SECURE_SETTINGS">Cambia le impostazioni del dispositivo</string>
<string name="android.permission.WRITE_SETTINGS">Cambia le impostazioni del dispositivo</string>
<string name="android10WifiToggleNotice">Sfortunatamente, Google ha deciso di rimuovere questa funzione in Android 10. Applicazioni normali non hanno più accesso alla accensione o spegnimento della connessione wifi on. Questo significa che questa azione non avrà effetto su questo dispositivo.</string>
<string name="android9RecordAudioNotice">Se si sta usando il livello di rumore come evento di attivazione: sfortunatamente, a partire da Android 9 (Pie), Google ha deciso di non permettere l\'accesso al microfono da parte di applicazioni attive in secondo piano. Pertanto questo evento non ha più effetto e non attiverà nessuna azione.</string>
<string name="anotherAppIsRunning">Si è avviata/fermata un\'altra app</string>
<string name="anotherPoiByThatName">Nome già usato per un\'altra posizione.</string>
<string name="anotherProfileByThatName">Nome già usato per un altro profilo.</string>
<string name="anotherRuleByThatName">Nome già usato per un\'altra regola.</string>
<string name="any">qualsiaisi</string>
<string name="anyApp">Qualsiasi applicazione</string>
<string name="anyDevice">qualsiasi dispositivo</string>
<string name="anyLocation">qualsiasi posizione</string>
<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="appStarted">App avviata.</string>
<string name="appStopped">App terminata.</string>
<string name="app_name">Automation</string>
<string name="appRequiresPermissiontoAccessExternalStorage">Automation richiede accesso alla memoria esterna per leggere le proprie impostazioni e regole.</string>
<string name="application">Applicazione</string>
<string name="applicationHasBeenUpdated">L\'applicazione è stata aggiornata.</string>
<string name="applyingSettingsAndRules">Applicazione delle impostazioni, regole e posizioni.</string>
<string name="areYouSure">Sicuro?</string>
<string name="at">il</string>
<string name="atLeastRuleXisUsingY">Almeno una regola ( \"%1$s\" ) sta usando una condizione di tipo \"%2$s\".</string>
<string name="logAttemptingDownloadOf">Tentativo di dowload di</string>
<string name="logAttemptingToBindToService">Tentativo di attivare il servizio... </string>
<string name="logAttemptingToUnbindFromService">Tentativo di disattivare il servizio... </string>
<string name="audibleSelection">Audio alla selezione di cambio schermo</string>
<string name="apply">applica</string>
<string name="areYouSure">Sei sicuro?</string>
<string name="at">al</string>
<string name="audibleSelection">Audio abilitato quando si seleziona lo schermo</string>
<string name="autoBrightness">Abilitare luminosità automatica</string>
<string name="autoBrightnessNotice">Se usi la luminosità automatica, il valore di luminosità scelto a seguito non sarà probabilmente in use per molto.</string>
<string name="batteryLevel">livello della batteria</string>
<string name="bluetoothConnection">Connessione Bluetooth</string>
<string name="bluetoothConnectionTo">Connessione Bluetooth fra %1$s</string>
<string name="bluetoothConnectionTo">Connessione Bluetooth con %1$s</string>
<string name="bluetoothDeviceInRange">Dispositivo Bluetooth %1$s rilevato.</string>
<string name="bluetoothDeviceOutOfRange">Dispositivo Bluetooth %1$s non raggiungibile.</string>
<string name="bluetoothDisconnectFrom">Bluetooth connection to %1$s torn</string>
<string name="bluetoothDisconnectFrom">Connessione Bluetooth con %1$s interrotta</string>
<string name="bluetoothFailed">Impossibile attivare il Bluetooth. Questo dispositivo è dotato di Bluetooth?</string>
<string name="logBoundToService">Servizio impegnato.</string>
<string name="cancel">Abbandona</string>
<string name="cantDownloadTooFewRequestsInSettings">Non è possibile scaricare nulla. La quantità di richieste HTTP è impostata inferiore a 1.</string>
<string name="cantMoveDown">Non posso abbassare l\'item. E\' già l\'ultimo.</string>
<string name="cantMoveUp">Non posso alzare l\'item. E\' già il primo.</string>
<string name="cantRunRule">Queste regole non possono essere eseguite.</string>
<string name="brightnessAuto">luminosità automatica</string>
<string name="brightnessManual">luminosità manuale</string>
<string name="cancel">Annulla</string>
<string name="cantFindSoundFile">Impossibile trovate il file audio %1$s e riprodurlo.</string>
<string name="cantMoveDown">Non posso spostare giù l\'elemento. E\' già l\'ultimo.</string>
<string name="cantMoveUp">Non posso spostare su l\'elemento. E\' già il primo.</string>
<string name="cantRunRule">Impossibile eseguire le regole.</string>
<string name="cantStopIt">Non posso fermarla.</string>
<string name="cellMastChanged">Devi cambiare la cella: %1$s</string>
<string name="cellMastIdleTime">Massimo tempo di inattività della cella</string>
<string name="change">Abilita modifiche</string>
<string name="logClearingBothLocationListeners">Cancello la posizione sia come rete che come GPS</string>
<string name="closeTo">chiuso in</string>
<string name="closestPoi">Posizione precedente:</string>
<string name="cellMastIdleTime">Massimo tempo di inattività del ripetitore</string>
<string name="change">Modifica</string>
<string name="chooseActivityHint">In questa ultima selezione dovrai selezionare una attività specifica. Praticamente, questa è come una finestra dell\'applicazione desiderata. Se non sai qual è, si raccomanda scegliere quella che ha \"main\" o \"launcher\" nel suo nome.</string>
<string name="clickAndHoldForOptions">Clicca e mantieni premuto un elemento per vedere le opzioni.</string>
<string name="closeTo">vicino a</string>
<string name="closestPoi">Posizione più vicina:</string>
<string name="com.wireguard.android.permission.CONTROL_TUNNELS">Controlla i tunnels dell\'applicazione wireguard</string>
<string name="comparing">Ho la posizione sia dalla rete che dal GPS e le sto confrontando...</string>
<string name="configurationExportedSuccessfully">Configurazione esportata con successo.</string>
<string name="configurationImportedSuccessfully">Configurazione esportata con successo.</string>
<string name="connected">connesso</string>
<string name="connectedToWifi">connesso a wifi \"%1$s</string>
<string name="connectedToWifi">connesso al wifi \"%1$s</string>
<string name="connecting">connessione in corso</string>
<string name="connectionToDevice">connessione al dispositivo</string>
<string name="continueText">Continua</string>
<string name="continueText">continua</string>
<string name="currentId">ID corrente:</string>
<string name="currentVolume">Volume attuale</string>
<string name="dataConWithRootFail">Il dato non può essere cambiato usando i permessi superuser.</string>
<string name="dataConWithRootSuccess">La connessione è stata cambiata usando i permessi superuser. </string>
<string name="deactivate">Disattivazione</string>
<string name="deactivated">non attivo</string>
<string name="deactivating">Disattivazione in corso</string>
<string name="defaultSettings">Ripristino impostazioni di default</string>
<string name="delete">cancella</string>
<string name="deleteCapital">Cancella</string>
<string name="delete">rimuovi</string>
<string name="deleteCapital">Rimuovi</string>
<string name="deletePoi">Cancella posizione</string>
<string name="detectedActivity">Cancella attività:</string>
<string name="detectedActivity">Attività rilevata:</string>
<string name="detectedActivityInVehicle">In un veicolo (auto, bus,...)</string>
<string name="detectedActivityInvalidStatus">Attività non valida</string>
<string name="detectedActivityOnBicycle">In bicicletta</string>
<string name="detectedActivityOnFoot">A piedi</string>
<string name="detectedActivityRunning">Di corsa</string>
<string name="detectedActivityStill">Fermo</string>
<string name="detectedActivityTilting">Pendenza</string>
<string name="detectedActivityTilting">Ribaltando</string>
<string name="detectedActivityUnknown">Sconosciuto</string>
<string name="detectedActivityWalking">Passeggio</string>
<string name="logDetectingTetherableUsbInterface">Individuata interfaccia USB con tethering.</string>
<string name="deviceDoesNotHaveNfc">Sembra che il dispositivo non sia NFC.</string>
<string name="detectedActivityWalking">Camminando</string>
<string name="deviceDoesNotHaveBluetooth">Questo dispositivo non sembra avere bluetooth. Puoi comunque configurare questa opzione, ma non avrà probabilmente nessun effetto.</string>
<string name="deviceDoesNotHaveNfc">Sembra che il dispositivo non abbia la funzione NFC.</string>
<string name="deviceInRange">dispositivo visibile</string>
<string name="deviceOutOfRange">dispositivo fuori portata</string>
<string name="direction">Direzione</string>
<string name="directionStringContains">contiene</string>
<string name="directionStringEndsWith">finisce in</string>
<string name="directionStringEquals">uguale a</string>
<string name="directionStringNotEquals">non uguale a</string>
<string name="directionStringStartsWith">comincia con</string>
<string name="disabledFeatures">Funzioni disabilitate</string>
<string name="disconnected">disconnesso</string>
<string name="disconnectedFromWifi">disconnesso dal wifi \"%1$s</string>
<string name="disconnectedFromWifi">disconnesso dalla rete wifi \"%1$s</string>
<string name="disconnecting">disconnessione in corso</string>
<string name="disconnectionFromDevice">disconnesso dal dispositivo</string>
<string name="distanceBetween">La distanza tra la posizione GPS e la posizione di rete è</string>
<string name="displayNewsOnMainScreen">Visualizza le novità dell\'applicazione sullo schermo principale</string>
<string name="displayNewsOnMainScreenDescription">Novità relative solo a questa applicazione, stiamo parlando di un paio per anno, non di più.</string>
<string name="distanceBetween">La distanza tra la posizione GPS e la posizione di rete è di %1$d metri. Il raggio minimo è +1 metro.</string>
<string name="distanceForGpsUpdate">Distanza per l\'aggiornamento del GPS [m]</string>
<string name="distanceForNetworkUpdate">Distanza per l\'aggiornamento della rete [m]</string>
<string name="droppingBelow">inferiore a</string>
<string name="droppingBelow">sta andando sotto</string>
<string name="dropsBelow">inferiore</string>
<string name="edit">Modifica</string>
<string name="end">Fine</string>
<string name="enforcingGps">Impedisce la localizzazione del provider e forza il GPS</string>
<string name="enterAPositiveValidNonDecimalNumber">Inserire un numero intero positivo</string>
<string name="enterAname">Inserisci un nome</string>
<string name="enterNameForIntentPair">Definisci un nome per la coppia di Intent</string>
<string name="enterPhoneNumber">Inserisci un numero di telefono. Lascia vuoto per qualsiasi numero.</string>
<string name="enterValidReferenceValue">Inserire un valore di riferimento valido.</string>
<string name="enterValueForIntentPair">Definisci un valore per la coppia di Intent.</string>
<string name="enterWifiName">Inserire un nome per la wifi. Se vuoto varrà per qualsiasi wifi.</string>
<string name="enterAname">Inserisci un nome!</string>
<string name="enterNameForIntentPair">Definisci un nome per la coppia di intenzioni</string>
<string name="enterPackageName">Inserisci un nome di pacchetto che sia valido.</string>
<string name="enterPhoneNumber">Inserisci un numero di telefono. Lascia vuoto per accettare qualsiasi numero.</string>
<string name="enterValidAction">Inserisci una azione valida</string>
<string name="enterValidReferenceValue">Inserisci un valore di riferimento valido.</string>
<string name="enterValueForIntentPair">Definisci un valore per la coppia di Intenzioni.</string>
<string name="enterWifiName">Inserire un nome per la wifi. Se vuoto sarà valido per qualsiasi wifi.</string>
<string name="entering">entrando in</string>
<string name="eraseSettings">Azzera le impostazioni</string>
<string name="error">Errore</string>
<string name="errorActivatingProfile">Errore nell\'attivazione del profilo:</string>
<string name="errorActivatingWifiAp">Errore nell\'attivazione del wifi</string>
<string name="errorChangingScreenRotation">Errore nella rotazione dello schermo</string>
<string name="logErrorDeterminingCurrentUsbTetheringState">Errore nel riconsocimento di UsbTethering.</string>
<string name="errorDeterminingWifiApState">Errore nel riconoscimento wifi</string>
<string name="logErrorGettingConnectionManagerService">Errore del connectionManager. Il Tethering USB non risponde.</string>
<string name="errorInitializingSettingsToPersistentMemory">Errore nello scrivere le impostazioni nella memoria di massa.</string>
<string name="errorActivatingWifiAp">Errore nell\'attivazione del punto di accesso wifi</string>
<string name="errorChangingScreenRotation">Errore nel ruotare lo schermo</string>
<string name="errorDeterminingWifiApState">Errore nel riconoscimento del punto di accesso wifi</string>
<string name="errorReadingPoisAndRulesFromFile">Errore nella lettura di regole e posizioni dal file.</string>
<string name="errorReadingSettings">Errore nel leggere le impostazioni</string>
<string name="errorRunningRule">C\'è stato un errore cercando di eseguire una regola.</string>
<string name="errorStartingOtherActivity">Errore nel\'avvio dell\'altra attività</string>
<string name="errorTriggeringUrl">Errore di indirizzamento</string>
<string name="errorWritingConfig">Errore nello scrivere la configurazione. La memoria è accessibile?</string>
<string name="errorWritingConfig">Errore nello scrivere la configurazione. È la memoria in sola lettura?</string>
<string name="errorWritingFile">Errore nella scrittura delle impostazioni.</string>
<string name="errorWritingSettingsToPersistentMemory">Errore nella memorizzazione delle impostazioni.</string>
<string name="exceeding">superiore a</string>
<string name="exceeds">superiore</string>
<string name="exceeding">sta sorpassando</string>
<string name="exceeds">è superiore</string>
<string name="executeRulesAndProfilesWithSingleClickTitle">Esegui regole/profili con un singolo click.</string>
<string name="exportConfiguration">Esporta la configurazione</string>
<string name="failedToTriggerBluetooth">Non riesco ad attivare il Bluetooth. Questo dispositivo ha il Bluetooth?</string>
<string name="featureNotInFdroidVersion">Questa funzione sfrutta software non basato su codice aperto. Pertanto non è disponibile nella versione F-Droid.</string>
<string name="featuresDisabled">L\'applicazione è in esecuzione in modalità limitata a causa di autorizzazioni mancanti.</string>
<string name="fileDoesNotExist">Il file non esiste.</string>
<string name="filesHaveBeenMovedTo">Automation usa adesso un percorso nuovo per salvare i tuoi files. Tutti i files di Automation sono stati mossi qui: \"%s\". I permessi di accesso alla memoria esterna non sono più necessari e si possono revocare. Questo verrà permanentemente rimosso in una versione futura.</string>
<string name="filesStoredAt">I files di configurazione e log sono salvati nella cartella %1$s. Clicca su questo testo per aprire l\'esploratore di files. Sfortunatamente, questo solo funziona su un dispositivo con accesso root.\n\nPER TUTTI GLI ALTRI DISPOSITIVI: basta usare il bottone \'esporta\' per fare un backup.</string>
<string name="forcedLocationUpdate">Aggiornamento forzato della posizione</string>
<string name="forcedLocationUpdateLong">Essendo scaduto il timeout della misura sarà considerata valida l\'ultima posizione rilevata.</string>
<string name="forcedLocationUpdateLong">Avendo raggiunto il timeout comparando le misure, sarà considerata valida l\'ultima posizione rilevata.</string>
<string name="friday">Venerdì</string>
<string name="general">Descrizione della regola</string>
<string name="from">da</string>
<string name="general">Generale</string>
<string name="generalSettings">Impostazioni globali</string>
<string name="generalText">Per utilizzare questo programma è necessario impostare delle regole. Le regole contengono eventi, per esempio l\'arrivare in una determinata zona o essere in un intervallo temporale. Dopo che è stato fatto clic sul pulsante ON / OFF nella home-page.
</string>
<string name="generalText">Per utilizzare questo programma è necessario impostare delle regole. Le regole contengono eventi, per esempio l\'arrivare in una determinata zona o essere in un intervallo temporale. Dopo che è stato fatto, fare clic sul pulsante ON / OFF nella home-page.</string>
<string name="getCurrentPosition">Rileva la posizione attuale</string>
<string name="gettingListOfInstalledApplications">Sto cercando le applicazioni installate … </string>
<string name="gettingPosition">Sto rilevando la posizione. Attendere prego ...</string>
<string name="logGettingPositionWithProvider">Richiesta posizione dal provider:</string>
<string name="googleLocationChicanery">Questa applicazione accumula dati di localizzazione per abilitare regole basate sulla posizione insieme al rilevamento della velocità anche quando l\'applicazione è chiusa o non in uso.</string>
<string name="googleLocationChicaneryOld">Questa applicazione accumula dati di localizzazione per determinare se sei attualmente ad una delle posizioni che hai creato. Inoltre, questa funzione è usata per determinare la tua velocità attuale se tale evento è stato attivato nelle regole. Questi rilevamenti sono effettuati anche quando l\'applicazione è chiusa o non in uso (ma solo se il servizio è attivato).</string>
<string name="googleSarcasm">Grazie alla infinita sapienza di Google e continui sforzi per proteggere la privacy di ognuno di noi, nelle regole che possano inviare SMS o coinvolgere lo stato del telefono, sono state rimossi eventi ed azioni rilevanti.</string>
<string name="google_app_id">id della tua app</string>
<string name="logGotGpsUpdate">GPS aggiornato. Precisione:</string>
<string name="logGotNetworkUpdate">Rete aggiornata. Precisione:</string>
<string name="gpsAccuracy">Precisone del GPS [m]</string>
<string name="gpsComparison">Confronto col GPS</string>
<string name="gpsComparisonTimeoutStop">Fermata la comparazione col GPS per timeout.</string>
<string name="gpsMeasurement">Misura GPS</string>
<string name="gpsMeasurementTimeout">Misura GPS fermata per timeout.</string>
<string name="hapticFeedback">Sensazione tattile(vibrazione al tocco)</string>
<string name="gpsComparison">Comparazione GPS</string>
<string name="gpsComparisonTimeoutStop">Sto fermando la comparazione con il GPS a causa di un timeout.</string>
<string name="hapticFeedback">Sensazione tattile (vibrazione al tocco)</string>
<string name="headphoneAny">Oppure</string>
<string name="headphoneMicrophone">Microfono</string>
<string name="headphoneSelectType">Seleziona il tipo di auricolare</string>
<string name="headphoneSimple">Auricolari</string>
<string name="headsetConnected">Connesso auricolari (type: %1$s) </string>
<string name="headsetDisconnected">Disconnesso auticolari (type: %1$s) </string>
<string name="helpTextPoi">Una posizione è composta da una zona con un raggio intorno a un punto specificato dalle coordinate GPS. ll posizionamento è realizzato appoggianodosi alle coordinate dei ripetitori del tuo gestore. E\' leggermente impreciso, ma veloce e consuma poca batteria. Pertanto è bene non specificare un raggio troppo piccolo. L\'applicazione suggerisce un raggio minimo quando si crea una nuova posizione.</string>
<string name="helpTextProcessMonitoring">Se si specifica una regola che controlli l\'esecuzione di un processo Automation eseguirà la verifica ogni x secondi (con x selezionabile nelle impostazioni). Va infatti considerato che un monitoraggio costante provocherebbe un rapido esaurimento della batteria e non esistono segnalazioni di questo tipo nel sistema operativo.</string>
<string name="helpTextRules">Tutti gli eventi di una regola sono efficaci se si verificano nella loro totalità. Basta che un evento non sia verificato è la regola non si attiverà. Per avere invece una funzionalità alternativa bisogna creare più regole.</string>
<string name="helpTextTimeFrame">Se si specifica una regola con un intervallo temporale si hanno due scelte. È possibile scegliere se si desidera attivare la regola all\'interno o all\'esterno dell\'intervallo di tempo. In entrambi i casi l\'azione verrà eseguita una sola volta.
Quindi, se si crea una regola che imposta il profilo su vibrazione nell\'intervallo temporale xyz, il telefono, passato allo stato vibrazione, rimarrà definitivamente in tale stato anche dopo lo scadere dell\'intervallo di tempo. Se si desidera che ciò avvenga è necessario specificare un\'altra regola con un altro periodo di tempo.</string>
<string name="helpTextToggable">Alcune regole hanno una bandiera chiamata "Reversibile". Ciò significa che, se una regola viene eseguita al verificarsi di un evento e poi quest\'ultimo si verifica una seconda volta, il comando della regola verrà eseguito una ulteriore volta in modalità inversa, se possibile. Attualmente questo avverrà solo in combinazione con i tag NFC. Se li si tocca due volte la regola associata invertirà la situazione attuale. Per esempio una regola “Reversibile” può disattivare il WiFi se attivo e viceversa attivarlo se non attivo.</string>
<string name="headsetConnected">Connesso auricolare (tipo: %1$s) </string>
<string name="headsetDisconnected">Disconnesso auticolari (tipo: %1$s) </string>
<string name="helpTextActivityDetection">Questa funzione può identificare se sei attualmente in movimento e se sei a piedi o in che tipo di veicolo (almeno con una certa precisione). Questa funzione non è completamente integrata con Automation, ma viene fornita dai servizi di Google Play. In pratica, non fornisce un risultato si/no, ma provvede una percentuale del livello di sicurezza dello stato di movimento identificato. Puoi scegliere la percentuale raggiunta la quale Automation accetterà un resultato. Nota: 1) è possibile che più di uno stato sia identificato nello stesso momento. Per esempio, potresti star CAMMINANDO dentro a un bus in movimento; 2) Questo sensor usa molta energia. Se possibile, potresti considerare delle alternative, per esempio, potresti identificare che stai guidando, forzando una connessione con il vivavoce.</string>
<string name="helpTextEnergySaving">Molti produttori di dispositive Android cercano di salvare energia limitando le attività di applicazioni eseguite in secondo piano. Sfortunatamente, questo spesso fa che tali applicazioni non funzionino correttamente e Automation è fra queste. Puoi leggere questa <a href="https://dontkillmyapp.com/">pagina web</a> per scoprire come escludere Automation da queste funzioni di risparmio energetico.</string>
<string name="helpTextPoi">Una posizione è composta da cooridinate GPS ed un raggio d\'azione. Dato che il posizionamento realizzato tramite i ripetitori del tuo gestore è piuttosto impreciso (ma veloce e consuma poca batteria), è bene non specificare un raggio troppo piccolo. L\'applicazione suggerisce un raggio minimo quando si crea una nuova posizione.</string>
<string name="helpTextProcessMonitoring">Se si specifica una regola che controlli l\'esecuzione di un processo, Automation eseguirà la verifica ogni x secondi (con x selezionabile nelle impostazioni). Bisogna considerare che un monitoraggio costante provocherebbe un rapido esaurimento della batteria e non esistono notifiche di questo tipo di attività proviste dal sistema operativo.</string>
<string name="helpTextRules">Una regola sarà eseguita quando tutti i suoi eventi risultano veri. Basta che solo un evento non sia eseguito e la regola non si attiverà. Per eseguire una regola in base a diversi eventi individuali, è sufficiente creare regole specifiche per ogni set di eventi.</string>
<string name="helpTextSound">Nello schermo principale puoi bloccare temporaneamente i cambi ai suoni per evitare l\'esecuzione di regole che facciano cambi alle attività sonore. Per esempio, potresti essere in una situatione o in un luogo dove normalmente ascoltare il suono di una suoneria è ok, ma in questa occasione bisognerebbe evitarlo. Questa funzione si disattiverà automaticamente non appena sia trascorso il tempo selezionato. Fai Click sul bottone + per raggiungere la quantità di tempo desiderata. Una volta attiva, questa si può disattivare nuovamente usando il pulsante di attivazione (e in questo modo, si riattiveranno le regole basate su cambi sonori).</string>
<string name="helpTextTimeFrame">Se si specifica una regola con un intervallo temporale si hanno due scelte. È possibile scegliere se si desidera attivare la regola all\'interno o all\'esterno dell\'intervallo di tempo. In entrambi i casi l\'azione verrà eseguita una sola volta. Quindi, se si crea una regola che imposta il profilo su vibrazione nell\'intervallo temporale xyz, il telefono, passato allo stato vibrazione, rimarrà definitivamente in tale stato anche dopo lo scadere dell\'intervallo di tempo. Se si desidera che ciò avvenga è necessario specificare un\'altra regola con un altro periodo di tempo.</string>
<string name="helpTextToggable">Le regole hanno un segno di spunta chiamato "Reversibile". Ciò significa che, se una regola viene eseguita al verificarsi di un evento e poi quest\'ultimo si verifica una seconda volta, il comando della regola verrà eseguito una ulteriore volta in modalità inversa, se possibile. Attualmente questo avverrà solo in combinazione con i tag NFC. Se li si tocca due volte la regola associata invertirà la situazione attuale. Per esempio una regola “Reversibile” può disattivare il WiFi se attivo e viceversa attivarlo se non attivo.</string>
<string name="helpTitleEnergySaving">Risparmio energetico</string>
<string name="hint">Suggerimento</string>
<string name="httpAcceptAllCertificatesSummary">Salta il controllo dei certificate SSL (si consiglia di non attivarlo)</string>
<string name="httpAcceptAllCertificatesTitle">Accetta tutti I certificati</string>
<string name="httpAttemptGapSummary">Pausa prima del successive tentativo [secondi]</string>
<string name="httpAcceptAllCertificatesSummary">Salta il controllo dei certificati SSL (si consiglia di non attivarlo)</string>
<string name="httpAcceptAllCertificatesTitle">Accetta tutti i certificati</string>
<string name="httpAttemptGapSummary">Pausa prima del tentativo successivo [secondi]</string>
<string name="httpAttemptGapTitle">Pausa [sec]</string>
<string name="httpAttemptsSummary">Numero di tentative ripetuti in caso di fallimento richiesta HTTP a causa della connessione </string>
<string name="httpAttemptsSummary">Numero di tentativi in caso le richieste HTTP falliscano a causa di problemi di connessione </string>
<string name="httpAttemptsTimeoutSummary">Timeout per richieste HTTP [secondi]</string>
<string name="httpAttemptsTimeoutTitle">Timeout [sec]</string>
<string name="httpAttemptsTitle">Numero di tentativi HTTP</string>
<string name="ignoringActivityDetectionUpdateTooSoon">Nessuna risposta. Riprovo tra %1$s secondi.</string>
<string name="incoming">ricevuta</string>
<string name="incomingAdjective">ricevuta</string>
<string name="importConfiguration">Importa configurazione</string>
<string name="importExportExplanation">Quando si clicca su importa o esporta, stai scegliendo la direzione in cui i files vengono importati o esportati. Quando si procede alla esportazione, files esistenti potrebbero essere sovrascritti.</string>
<string name="importNumberFromContacts">Importa numero dai contatti</string>
<string name="incoming">in arrivo</string>
<string name="incomingAdjective">in arrivo</string>
<string name="incomingCallFrom">Chiamata in arrivo da %1$s.</string>
<string name="incomingCallsRingtone">Suoneria per le chiamate in arrivo</string>
<string name="initializingSettingsToPersistentMemory">Impostazioni iniziali in una memoria di massa.</string>
<string name="insideOrOutsideTimeFrames">Dentro o fuori l\'intervallo?</string>
<string name="insideOrOutsideTimeFrames">Dentro o fuori questi intervalli?</string>
<string name="intentDataComment">Se il tuo parametro è di tipo Uri e usi \"IntentData\" come nome (in maiuscole o minuscole non importa), il parametro non verrà aggiunto come parametro normale con putExtra(), ma sarà aggiunto all\'intento con setData().</string>
<string name="invalidDevice">Dispositivo non valido.</string>
<string name="invalidPoiName">Nome posizione non valido.</string>
<string name="invalidProfileName">Nome profilo non valido.</string>
<string name="invalidStuffStoredInSettingsErasing">Impostazioni non tutte valide. Le sto cancellando ...</string>
<string name="is">é</string>
<string name="is">è</string>
<string name="lastRule">Ultima regola:</string>
<string name="latitude">Latitudine</string>
<string name="leaving">uscendo da</string>
@ -245,287 +270,306 @@ Quindi, se si crea una regola che imposta il profilo su vibrazione nell\'interva
<string name="lengthOfNoiseLevelMeasurementsTitle">Durata di ogni misura di livello di rumore</string>
<string name="listenToAccelerometerState">Rileva i movimenti del dispositivo dove non fosse disponibile il wifi</string>
<string name="listenToWifiState">Rileva il cambiamento di stato del wifi se disponibile</string>
<string name="loadWifiList">Carica lista wifi</string>
<string name="locationDisabled">Localizzazione disattivata</string>
<string name="locationEngineDisabledLong">Purtroppo la tua posizione non può più essere determinata. Un debito di gratitudine è dovuto a Google per la sua infinita saggezza e amabilità.\n\nVediamo di approfondire questo problema. A partire da Android 10 è stato introdotto un nuovo permesso che è necessario per determinare la vostra posizione in secondo piano (che naturalmente è necessario per un\'app come questa). Sebbene la consideri una buona idea in generale, i problemi che comporta per gli sviluppatori non lo sono.\n\nWQuando si sviluppa un\'app si può cercare di qualificarsi per questo permesso rispettando un catalogo di requisiti. Purtroppo le nuove versioni della mia app sono state rifiutate per un periodo di tre mesi. Ho soddisfatto tutti questi requisiti, mentre il merdoso supporto allo sviluppo di Google ha affermato che non l\'ho fatto. Dopo aver dato loro la prova che l\'ho fatto - ho ottenuto una risposta del tipo \"non posso più aiutarti\". Alla fine mi sono arreso.\n\nDi conseguenza, la versione di Google Play non può più usare la tua posizione come trigger. La mia unica opzione alternativa sarebbe stata quella di avere questa applicazione rimossa dal negozio interamente.\n\nNe sono molto dispiaciuto, ma ho fatto del mio meglio per discutere con un \"supporto\" che ripetutamente non ha superato il test di Turing.\n\nInfine, c\'è una buona notizia: puoi ancora avere tutto!\n\nAutomation è adesso una applicazione di codice aperto e si può trovare su F-Droid. Questo è un app store che si preoccupa davvero della tua privacy - invece che fingere di farlo. Basta fare il backup del tuo file di configurazione, disinstallare questa app, installarla di nuovo da F-Droid e ripristinare il tuo file di configurazione - fatto.\n\nClicca qui per maggiori informazioni:</string>
<string name="locationEngineDisabledShort">La localizzazione non può più essere determinata. Clicca qui per scoprirne il perché.</string>
<string name="locationEngineNotActive">Ricerca posizione non attiva.</string>
<string name="lockSoundChanges">Cambiamento dei suoni:</string>
<string name="lockSoundChanges">Blocca il cambio dei suoni:</string>
<string name="logFileMaxSizeSummary">Massima dimensione del file di log in Megabyte. Tronca quando la dimensione eccede.</string>
<string name="logFileMaxSizeTitle">Massima dimensione del file di log [Mb]</string>
<string name="logFileMaxSizeTitle">Massima dimensione del file di log [Mb]</string>
<string name="logLevelSummary">Dettaglio del file di Log (1=minimo, 5=massimo)</string>
<string name="logLevelTitle">Dettaglio del file di log</string>
<string name="logServiceStopping">Arrestando il servizio.</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>
<string name="minimumTimeForLocationUpdates">Intervallo minimo in secondi per aggiornare la localizzazione</string>
<string name="mainScreenPermissionNote">Automation richiede ulteriori autorizzazioni per funzionare correttamente. Clicca su questo testo per saperne di più e richiederle.</string>
<string name="manageLocations">Creare o modificare posizioni</string>
<string name="matching">abbinando</string>
<string name="messageNotShownAgain">Questo messaggio non sarà mostrato più.</string>
<string name="minimumDistanceChangeForGpsLocationUpdates">Minimo intervallo (in metri) per aggiornare le posizioni GPS </string>
<string name="minimumDistanceChangeForNetworkLocationUpdates">Minimo cambio della distanza per aggiornare la posizione dalla rete.</string>
<string name="minimumTimeForLocationUpdates">Intervallo minimo in millisecondi per aggiornare la localizzazione</string>
<string name="monday">Lunedì</string>
<string name="moreSettings">Altre impostazioni</string>
<string name="moveDown">Sposta verso il basso</string>
<string name="moveUp">Sposta versol\'alto</string>
<string name="muteTextToSpeechDuringCallsSummary">Disabilitazione del TextToSpeach durante le chiamate</string>
<string name="muteTextToSpeechDuringCallsTitle">Silenziato durante la chiamata</string>
<string name="muteTextToSpeechDuringCallsTitle">Silenziare durante le chiamate</string>
<string name="name">Nome</string>
<string name="needLocationPermForWifiList">L\'elenco delle wifi a cui il tuo dispositivo è stato connesso potrebbe essere usato per determinare i luoghi in cui sei stato. Questo è il motivo per cui il permesso di localizzazione è richiesto per caricare l\'elenco delle wifi. Se vuoi essere in grado di sceglierne una dalla lista devi concedere questo permesso. Altrimenti puoi ancora inserire il nome della tua wifi manualmente.</string>
<string name="networkAccuracy">Precisione della rete [m]</string>
<string name="newId">Nuovo ID:</string>
<string name="newNfcId">Scrivi un nuovo ID NFC</string>
<string name="newThreadRules">Nuova discussione</string>
<string name="newsOptIn">Vuoi ricevere delle notizie su questa app (solo quelle importanti) nella schermata principale? Queste vengono scaricate dal sito web dello sviluppatore. Non ci sarà nessuna notifica intrusiva, solo un testo nella schermata principale quando apri l\'app.</string>
<string name="nfcApplyTagToRule">Applicazione del tag alla regola</string>
<string name="nfcBringTagIntoRange">Portarsi nel campo d\'azione di un tag NFC.</string>
<string name="nfcBringTagIntoRangeToRead">Ricerca di un TAG da leggere.</string>
<string name="nfcEnterValidIdentifier">Inserire un nome valido per il tag (come "porta d\'ingresso di casa").</string>
<string name="nfcNoNdefIntentBut">Nessun NFC NDEF intent, ma</string>
<string name="nfcBringTagIntoRange">Portare un tag NFC nel campo d\'azione.</string>
<string name="nfcBringTagIntoRangeToRead">Avvicina il TAG da leggere.</string>
<string name="nfcEnterValidIdentifier">Inserire un nome valido per il tag (come "Porta d\'ingresso di casa").</string>
<string name="nfcNoNdefIntentBut">Nessun intento NFC NDEF , ma</string>
<string name="nfcNoTag">Nessun tag rilevato.</string>
<string name="nfcNotSupportedInThisAndroidVersionYet">NFC non è ancora supportato in questa versione di Android.</string>
<string name="nfcReadTag">Lettura ID da un tag</string>
<string name="nfcNotSupportedInThisAndroidVersionYet">NFC non ancora supportato in questa versione di Android.</string>
<string name="nfcReadTag">Lettura ID dal tag</string>
<string name="nfcTag">Tag NFC</string>
<string name="nfcTagDataNotUsable">Dato del tag inadatto, riscrivere.</string>
<string name="nfcTagDataNotUsable">Dati del tag non leggibili, si prega di riscriverli.</string>
<string name="nfcTagDiscovered">Tag rilevato</string>
<string name="nfcTagFoundWithText">Trovatao Tag con scritto:</string>
<string name="nfcTagReadSuccessfully">Tag letto.</string>
<string name="nfcTagWriteError">Errore di scrittura sul tag. E\' in visibilità?</string>
<string name="nfcTagWrittenSuccessfully">Scrittura Tag verificata.</string>
<string name="nfcTagFoundWithText">Trovato Tag con testo:</string>
<string name="nfcTagReadSuccessfully">Tag letto con successo.</string>
<string name="nfcTagWriteError">Errore di scrittura sul tag. È sufficientemente vicino?</string>
<string name="nfcTagWrittenSuccessfully">Scrittura Tag eseguita con successo.</string>
<string name="nfcUnsupportedEncoding">Codifica non supportata:</string>
<string name="nfcValueNotSuitable">Valore memorizzato non adatto.</string>
<string name="nfcWriteTag">Scrittura tag</string>
<string name="no">No</string>
<string name="noApplicableFilesFoundInDirectory">Nessun file adatto è stato trovato in quella directory.</string>
<string name="noChangeSelectedProfileDoesntMakeSense">Nessun cambiamento selezionato. Questo profilo non ha senso.</string>
<string name="noDataChangedReadingAnyway">Sembra che non siano state salvate le modifiche. Tuttavia ci possono essere stati cambiamenti nella memoria che devono essere recuperati. Devo ricaricare il file.</string>
<string name="noDataChangedReadingAnyway">Sembra che non siano state salvate le modifiche. Tuttavia ci possono essere stati cambiamenti in memoria che devono essere rimossi. Devo ricaricare il file.</string>
<string name="noDevice">nessun dispositivo</string>
<string name="noFileManageInstalled">Nessun esploratore di files installato.</string>
<string name="noFilesImported">Non è stato possibile importare nessun file.</string>
<string name="noKnownWifis">Non c\'è nessuna wifi conosciuta sul tuo dispositivo.</string>
<string name="noMapsApplicationFound">Non trovo un navigatore installato.</string>
<string name="noOverLap">Nessuna duplicazione di posizioni rilevata.</string>
<string name="noPoiInRelevantRange">Nessuna posizione in un range significativo.</string>
<string name="noPoisDefinedShort">Nessuna posizione inidcata.</string>
<string name="noPoisSpecified">Non hai specificato nessuna posizione. E\' necessario.</string>
<string name="noProfileChangeSoundLocked">Il profilo non può essere attivato. Rimane attivo l\'ultimo profilo attivato.</string>
<string name="noProfilesCreateOneFirst">Non è specificato nessun profilo nella tua configurazione. Devi farlo prima.</string>
<string name="logNoSuitableProvider">Non posso localizzare attraverso il provider.</string>
<string name="noWifiNameSpecifiedAnyWillDo">Nessun SSID (nome della wifi) specificato; devi inserirne uno.</string>
<string name="noWritableFolderFound">Nessun folder disponibile per salvare il file di configurazione.</string>
<string name="noiseDetectionHint">Se pensi che la rilevazione del rumore non funzioni correttamente (in base al valore specificato) considera che ogni telefono è diverso. Quindi puoi tarare il "riferimento per la misurazione del rumore" nelle impostazioni. Consulta http://en.wikipedia.org/wiki/Decibel per maggiori informazioni. È possibile utilizzare la “Taratura audio” della schermata principale per calibrare il dispositivo.</string>
<string name="noPoiInRelevantRange">Nessuna posizione nel raggio specificato.</string>
<string name="noPoisDefinedShort">Nessuna posizione indicata.</string>
<string name="noPoisSpecified">Non hai specificato nessuna posizione. È necessario.</string>
<string name="noProfileChangeSoundLocked">Il profilo non può essere attivato. L\'ultimo profilo attivato è bloccato.</string>
<string name="noProfilesCreateOneFirst">Non è specificato nessun profilo nella tua configurazione. Prima di tutto, creane uno.</string>
<string name="noWifiNameSpecifiedAnyWillDo">Nessun nome della wifi specificato; devi inserirne uno.</string>
<string name="noWritableFolderFound">Nessuna cartella scrivibile trovata che permetta salvare il file di configurazione.</string>
<string name="noiseDetectionHint">Se pensi che la rilevazione del rumore non funzioni correttamente (in base al valore specificato) considera che ogni telefono è diverso. Quindi puoi tarare il "riferimento per la misurazione del rumore" nelle impostazioni. Consulta http://en.wikipedia.org/wiki/Decibel per maggiori informazioni. È possibile utilizzare la \"Taratura audio\" dalla schermata principale per calibrare il dispositivo.</string>
<string name="none">nessuno</string>
<string name="logNotAllMeasurings">P</string>
<string name="notEnforcingGps">Permette la localizzazione da terzi e la normale ricerca del provider.</string>
<string name="notRearmingProcessMonitoringMessageStopRequested">Messaggio di mancato avvio del monitoraggio, è riciesto l\arresto.</string>
<string name="logNotStartingServiceAfterAppUpdate">Nessun servizio attivo dopo laggiornamento dellApp.</string>
<string name="logNotStartingServiceAtPhoneBoot">Nessun servizio attivo allavvio del telefono.</string>
<string name="notAllFilesImported">Non è stato possibile importare tutti i file rilevanti.</string>
<string name="notification">Notifica</string>
<string name="notificationAppears">La notifica appare</string>
<string name="notificationDisappears">La notifica non appare</string>
<string name="notificationRingtone">Tono di notifica</string>
<string name="notificationTriggerExplanation">Questo evento risponderà ad altre applicazioni che aprono notifiche nell\'area apposita (o che vengono chiuse). Puoi specificare un\'altra applicazione da cui la notifica deve provenire. Se non lo fai, saranno incluse le notifiche da qualsiasi altra applicazione. Puoi anche specificare le stringhe che devono o non devono essere presenti nel titolo o nel corpo della notifica. Il confronto fatto non è sensibile alle maiuscole e alle minuscole.</string>
<string name="number">numero</string>
<string name="ok">Ok</string>
<string name="onOff">On/Off</string>
<string name="onOff">Acceso/Spento</string>
<string name="openExamplesPage">Apri la pagina web con gli esempi</string>
<string name="outgoing">effettuata</string>
<string name="outgoingAdjective">effettuata</string>
<string name="outgoingCallFrom">Ultima chiamata effettuata %1$s fa.</string>
<string name="outgoingCallTo">Ultima chiamata effettuata %1$s fa.</string>
<string name="overlapBetweenPois">Rilevata sovrapposizione della posizione %1$s. Ridurre il raggio almeno di %2$s metri.</string>
<string name="overview">Panoramica</string>
<string name="packageName">Nome del pacchetto</string>
<string name="parameterName">Nome parametro</string>
<string name="parameterType">Tipo parametro</string>
<string name="parameterValue">Valore</string>
<string name="password">Password</string>
<string name="periodicProcessMonitoringIsAlreadyRunning">Non posso avviare il processo ciclico di monitoraggio perché é già attivo.</string>
<string name="periodicProcessMonitoringIsNotActive">Non posso fermare il processo ciclico di monitoraggio perchè non è attivo.</string>
<string name="periodicProcessMonitoringStarted">Processo ciclico di monitoraggio avviato.</string>
<string name="periodicProcessMonitoringStopped">Processo ciclico di monitoraggio terminato.</string>
<string name="permissionsExplanation">Spiegazione delle autorizzazioni richieste</string>
<string name="permissionsExplanationGeneric">L\'applicazione è in esecuzione in modalità risparmio energetico ed ha pertanto disattivato alcune caratteristiche. Per funzionare appieno richiede ulteriori autorizzazioni. Se si desidera utilizzare tutte le funzionalità è necessario concedere le autorizzazioni indicate o alcune regole non potranno essere eseguite. Per ogni autorizzazione è esplicitato il motivo.
Selezionare su “Continua” quando si è pronti a procedere.</string>
<string name="permissionsExplanationSmall">Per attivare la funzione che hai tentato di utilizzare, necessitano ulteriori autorizzazioni. Seleziona “Continua” per richiederle.</string>
<string name="permissionsTitle">Occorrono le autorizzazioni.</string>
<string name="permissionsExplanationGeneric">L\'applicazione sta venendo attualmente eseguita in modalità limitata ed ha pertanto disattivato alcune funzioni. Per funzionare appieno richiede ulteriori permessi. Se vuoi utilizzare tutte le funzionalità è necessario concedere i permessi nelle successive finestre o alcune regole non potranno essere eseguite. Di seguito ti viene data una spiegazione dei permessi richiesti. Clicca su \"Continua\" quando sei pronto a procedere.</string>
<string name="permissionsExplanationSmall">Per attivare la funzione che hai appena tentato di utilizzare, sono necessari ulteriori permessi. Clicca \"Continua\" per richiederli.</string>
<string name="permissionsTitle">Permessi necessari</string>
<string name="phoneCall">Chiamata</string>
<string name="phoneDirection">Seleziona se entrante o uscente</string>
<string name="phoneIsNotRooted">Il telefono non è rootato.</string>
<string name="phoneIsRooted">Il telefono è rootato.</string>
<string name="phoneNrReplacementError">Non ho l\'ultimo numero di telefono e quindi non posso inserirlo nella variabile.</string>
<string name="phoneNumber">Numero di telefono</string>
<string name="phoneNumberExplanation">È possibile inserire un numero di telefono specifico, ma non è necessario. Se vuoi specificarne uno, puoi sceglierlo dalla tua rubrica o inserirlo manualmente. Inoltre puoi usare espressioni regolari. Per testare un\'espressione regolare mi piace questa pagina:</string>
<string name="playSound">Esegui suono</string>
<string name="pleaseEnterValidLatitude">Inserisci una latitudine valida.</string>
<string name="pleaseEnterValidLongitude">Inserisci una longitudine valida.</string>
<string name="pleaseEnterValidName">Inserisci un nome valido.</string>
<string name="pleaseEnterValidRadius">Inserisci un raggio positivo e valido.</string>
<string name="pleaseSpecifiyAction">Indica almeno un\'azione.</string>
<string name="pleaseSpecifiyTrigger">Indica almeno una consizione.</string>
<string name="pleaseSpecifiyTrigger">Indica almeno un evento.</string>
<string name="poi">Posizione</string>
<string name="poiCouldBeInRange">Almeno la posizione %1$s potrebbe essere in zona, se non in sovrapposizione.</string>
<string name="poiHasNoWifiNotStoppingCellLocationListener">La posizione non ha la connessione wifi. Continuo CellLocationListener.</string>
<string name="poiHasWifiStoppingCellLocationListener">La posizione ha la connessione wifi. Termino CellLocationListener.</string>
<string name="poiList">Elenco alfabetico delle posizioni:</string>
<string name="poiStillReferenced">Ci sono ancora regole che fanno riferimento alla posizione (%1$s). Quindi non posso cancellare.</string>
<string name="poiCouldBeInRange">Almeno la posizione %1$s potrebbe essere in zona, se non ne esistono altre in aggiunta.</string>
<string name="poiList">Elenco delle posizioni:</string>
<string name="poiStillReferenced">Ci sono ancora regole che fanno riferimento a questa posizione (%1$s). Quindi non posso cancellarla ancora.</string>
<string name="pois">Posizioni</string>
<string name="positioningEngine">Modulo di posizionamento</string>
<string name="positioningSettings">Impostazioni del posizionamento</string>
<string name="positioningThresholds">Soglia del posizionamento</string>
<string name="positioningWindowNotice">Se sei all\' interno di un edificio è fortemente consigliato accostarsi ad una finestra fino a quando è stata trovata una posizione. Diversamente la ricerca p richiedere molto tempo.</string>
<string name="privacy">Info Privacy</string>
<string name="positioningThresholds">Soglie del posizionamento</string>
<string name="positioningWindowNotice">Se sei all\' interno di un edificio è fortemente consigliato avvicinarsi ad una finestra fino a quando è stata trovata una posizione. Diversamente la ricerca potrebbe richiedere molto tempo o non riuscire a trovare nulla.</string>
<string name="postsNotification">%1$s notifica dei messaggi</string>
<string name="prefsImportError">C\'è stato un errore nell\'importazione delle preferenze.</string>
<string name="privacy">Informativa sulla Privacy</string>
<string name="privacyConfirmationText">Sarai reindirizzato al sito dello sviluppatore per scaricare l\'informativa sulla privacy.</string>
<string name="privacyLocationingSummary">Evita i metodi di localizzazione che possono inviare la tua posizione a un provider, ad esempio Google. Userà solo il GPS. Questo può provocare rallentamenti o non funzionare correttamente.</string>
<string name="privacyLocationingTitle">Solo posizioni riservate</string>
<string name="privacyLocationingSummary">Evita i metodi di localizzazione che possono inviare la tua posizione a un provider, ad esempio Google. Userà solo il GPS e pertanto potrebbe essere lento o non funzionare correttamente.</string>
<string name="privacyLocationingTitle">Solo posizioni private</string>
<string name="processMonitoring">Controllo di un processo</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>
<string name="publishedOn">pubblicato il</string>
<string name="radiusHasToBePositive">Il raggio deve essere un numero positivo.</string>
<string name="radiusWithUnit">Raggio [m]</string>
<string name="readLocation">Legge la posizione</string>
<string name="rearmingProcessMonitoringMessage">Messaggio di riavvio del monitoraggio.</string>
<string name="referenceValueForNoiseLevelMeasurementsSummary">Valore di riferimento fisico per la misura di rumore</string>
<string name="referenceValueForNoiseLevelMeasurementsTitle">Riferimento per la misura di rumore</string>
<string name="refreshingProcessList">Ricontrollo la lista dei processi.</string>
<string name="refreshingSettingsFromFileToMemory">Riprisitna in memoria le impostzioni memorizzate in un file.</string>
<string name="rememberLastActivePoiSummary">Se sei in una posizione, la memorizza in modo che al riavvio l\'applicazione eseguirà le regole associate al lasciare la posizione.</string>
<string name="rememberLastActivePoiTitle">Ricorda la posizione dell\'ultima attività</string>
<string name="rememberLastActivePoiSummary">Se sei in una posizione, riavvia il tuo dispositivo o l\'applicazione e lascia la posizione. L\'applicazione eseguirà le regole associate alla uscita dal luogo al suo prossimo avvio.</string>
<string name="rememberLastActivePoiTitle">Ricorda la ultima posizione attiva</string>
<string name="removedNotification">la notifica da %1$s rimossa</string>
<string name="ringing">squillando</string>
<string name="roaming">Roaming</string>
<string name="rootExplanation">È necessario rootare il telefono per utilizzare questa funzione. Una volta rootato il telefono devi "eseguire la regola manualmente" per attivare la richiesta di autorizzazione come superuser. E\' necessario autorizzare l\'applicazione a utilizzare il profilo superuser sempre. In caso contrario, la regola non p funzionare quando il telefono è inattivo.</string>
<string name="rootExplanation">È necessario avere permessi di root per utilizzare questa funzione. Una volta abilitato l\'accesso root, dovrai \"eseguire la regola manualmente\" per attivare la richiesta di autorizzazione come superuser. Quando la finestra di superuser appare, bisognerà autorizzare l\'applicazione a utilizzare superuser sempre. In caso contrario, la regola non potrà funzionare quando il telefono è inattivo.</string>
<string name="rule">Regola</string>
<string name="ruleActivate">Esecuzione di %1$s</string>
<string name="ruleActivateToggle">Esecuzione di %1$s come Toggle</string>
<string name="ruleActive">Attiva</string>
<string name="ruleActivate">Attivando la regola %1$s</string>
<string name="ruleActivateToggle">Attivando la regola %1$s in modalità reversibile</string>
<string name="ruleActive">Regola attiva</string>
<string name="ruleCheckOf">Controllo della regola %1$s</string>
<string name="ruleDoesntApplyActivityGivenButTooLowProbability">Regola inapplicabile. Attività %1$s rilevata, ma con una probabilità insufficente (%2$s %%), occorre almeno il %3$s %%.</string>
<string name="ruleDoesntApplyActivityNotPresent">Regola inapplicabile. Serve l\'attività %1$s non disponibile.</string>
<string name="ruleDoesntApplyBatteryHigherThan">Regola inapplicabile: Livello della batteria superiore a</string>
<string name="ruleDoesntApplyBatteryLowerThan">Regola inapplicabile: livello della batteria inferiore a</string>
<string name="ruleDoesntApplyDeviceInRangeButShouldNotBe">Regola inapplicabile. Incoerente il range del dispositivo</string>
<string name="ruleDoesntApplyItsLouderThan">Regola inapplicabile. E\' più forte di</string>
<string name="ruleDoesntApplyItsQuieterThan">Regola inapplicabile. E\' inferiore a</string>
<string name="ruleDoesntApplyNoTagLabel">Regola inapplicabile. Non vi è alcuna etichetta tag o nessun tag. </string>
<string name="ruleDoesntApplyNotTheCorrectDeviceAddress">Regola inapplicabile. Indirizzo dispositivo bluetooth errato.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceName">Regola inapplicabile. Nome dispositivo bluetooth errato.</string>
<string name="ruleDoesntApplyNotTheCorrectSsid">Regola inapplicabile. SSID errato (demanded: \"%1$s\", given: \"%2$s\").</string>
<string name="ruleDoesntApplyStateNotCorrect">Regola inapplicabile. Stato errato</string>
<string name="ruleDoesntApplyWeAreFasterThan">Regola inapplicabile. Velocità superiore a</string>
<string name="ruleDoesntApplyWeAreSlowerThan">Regola inapplicabile. Velocità inferiore a</string>
<string name="ruleDoesntApplyWrongHeadphoneType">Regola inapplicabile. Tipo di auricolare errato.</string>
<string name="ruleDoesntApplyWrongTagLabel">Regola inapplicabile. Etichetta Tag errata.</string>
<string name="ruleDoesntApplyBatteryHigherThan">Impossibile applicare la regola: Livello della batteria superiore a</string>
<string name="ruleDoesntApplyBatteryLowerThan">Impossibile applicare la regola: livello della batteria inferiore a</string>
<string name="ruleDoesntApplyDeviceInRangeButShouldNotBe">Impossibile applicare la regola. Il dispositivo è in portata, ma non dovrebbe esserlo</string>
<string name="ruleDoesntApplyItsLouderThan">Impossibile applicare la regola. È più forte di</string>
<string name="ruleDoesntApplyItsQuieterThan">Impossibile applicare la regola. È inferiore a</string>
<string name="ruleDoesntApplyNoTagLabel">Impossibile applicare la regola. Non vi è alcuna etichetta tag o nessun tag. </string>
<string name="ruleDoesntApplyNotTheCorrectDeviceAddress">Impossibile applicare la regola. Indirizzo dispositivo bluetooth errato.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceName">Impossibile applicare la regola. Nome dispositivo bluetooth errato.</string>
<string name="ruleDoesntApplyNotTheCorrectSsid">Impossibile applicare la regola. SSID errato (richiesto: \"%1$s\", ottenuto: \"%2$s\").</string>
<string name="ruleDoesntApplyStateNotCorrect">Impossibile applicare la regola. Stato errato</string>
<string name="ruleDoesntApplyWeAreFasterThan">Impossibile applicare la regola. Velocità superiore a</string>
<string name="ruleDoesntApplyWeAreSlowerThan">Impossibile applicare la regola. Velocità inferiore a</string>
<string name="ruleDoesntApplyWrongTagLabel">Impossibile applicare la regola. Etichetta Tag errata.</string>
<string name="ruleHistory">Cronologia delle regole (dalla più recente):</string>
<string name="ruleIsDeactivatedCantApply">La regola %1$s é disattiva e non posso applicarla.</string>
<string name="ruleLegend">Verde = attiva, Rosso = inattiva, Giallo = necessita ulterori autorizzazioni Android.</string>
<string name="ruleList">Elenco alfabetico delle regole:</string>
<string name="ruleIsDeactivatedCantApply">La regola %1$s è disabilitata e non posso applicarla.</string>
<string name="ruleLegend">Verde = abilitata, Rosso = disabilitata, Giallo = necessita ulteriori permessi</string>
<string name="ruleList">Elenco delle regole:</string>
<string name="ruleName">Nome della regola</string>
<string name="ruleNotToggable">La regola %1$s non è Reversibile.</string>
<string name="ruleToggable">La regola %1$s é Reversibile.</string>
<string name="ruleXrequiresThis">Lo richiede la regola \"%1$s\".</string>
<string name="ruleNotToggable">La regola %1$s non è adatta ad essere Reversibile.</string>
<string name="ruleToggable">La regola %1$s è adatta per essere Reversibile.</string>
<string name="ruleXrequiresThis">La regola \"%1$s\" ne ha bisogno.</string>
<string name="rules">Regole</string>
<string name="rulesImportError">C\'è stato un errore nell\'importazione di regole e posizioni</string>
<string name="rulesImportedSuccessfully">Le regole e le posizioni sono state importate con successo.</string>
<string name="runManually">Esecuzione manuale</string>
<string name="runningApp">App in esecuzione</string>
<string name="satisfactoryAccuracyGps">precisione minima in metri quando la posizione è individuata via GPS</string>
<string name="satisfactoryAccuracyNetwork">Precisione minima quando la localizzazione è effettuata in metri attraverso le celle radiomobile </string>
<string name="satisfactoryAccuracyGps">Precisione minima in metri quando la posizione è individuata via GPS</string>
<string name="satisfactoryAccuracyNetwork">Precisione minima quando la localizzazione è effettuata in metri attraverso i ripetitori</string>
<string name="saturday">Sabato</string>
<string name="save">Salva</string>
<string name="savePoi">Salva posizione</string>
<string name="saveRule">Conferma</string>
<string name="saveRule">Salva Regola</string>
<string name="screenLockSoundNotice">I suoni di blocco dello schermo non possono più essere modificati automaticamente sui dispositivi con Android versione 6.0 o superiore. Qualunque cosa tu abbia impostato qui, non funzionerà in nessuna direzione.</string>
<string name="screenLockUnlockSound">Suono di blocco/sblocco schermo</string>
<string name="screenRotationAlreadyDisabled">Rotazione schermo già disattiva.</string>
<string name="screenRotationAlreadyDisabled">Rotazione schermo già disattivata.</string>
<string name="screenRotationAlreadyEnabled">Rotazione schermo già attiva.</string>
<string name="screenRotationDisabled">Rotazione schermo disabilitata.</string>
<string name="screenRotationEnabled">Rotazione schermo attivata.</string>
<string name="selectActivityToBeStarted">Seleziona l\'attività del pacchetto scelto</string>
<string name="selectApplication">Aggiungi App</string>
<string name="selectApplication">Scegli App</string>
<string name="selectBattery">Seleziona il livello di batteria</string>
<string name="selectConnectionOption">Seleziona una opzione di connessione.</string>
<string name="selectDeviceFromList">uno della lista</string>
<string name="selectDeviceOption">Seleziona una opzione del dispositivo</string>
<string name="selectDeviceFromList">uno dalla lista</string>
<string name="selectDeviceOption">Seleziona una opzione di dispositivo</string>
<string name="selectNoiseLevel">Seleziona il livello di rumore</string>
<string name="selectOneDay">Seleziona almeno un giorno</string>
<string name="selectPackageOfApplication">Seleziona quale attività</string>
<string name="selectPackageOfApplication">Seleziona il pacchetto dell\'applicazione</string>
<string name="selectPoi">Seleziona la posizione</string>
<string name="selectSoundFile">Select sound file</string>
<string name="selectSoundProfile">Seleziona il profilo audio</string>
<string name="selectSpeed">Seleziona la velocità</string>
<string name="selectToggleDirection">Seleziona</string>
<string name="selectToggleDirection">Seleziona acceso o spento?</string>
<string name="selectTypeOfAction">Seleziona il tipo di azione</string>
<string name="selectTypeOfActivity">Seleziona il tipo di attività</string>
<string name="selectTypeOfIntentPair">Seleziona il tipo per la coppia di Intent</string>
<string name="selectTypeOfIntentPair">Seleziona il tipo per la coppia di Intenzioni</string>
<string name="selectTypeOfTrigger">Seleziona il tipo di evento</string>
<string name="service">Stato:</string>
<string name="logServiceAlreadyRunning">Richiesta di attivazione sul servizio già attivo.</string>
<string name="sendTextMessage">Invia messaggio di testo</string>
<string name="service">Servizio:</string>
<string name="serviceHasToRunForThat">Devi attivare il servizio.</string>
<string name="serviceNotRunning">Servizio non attivo.</string>
<string name="serviceStarted">Automation attivata.</string>
<string name="version">Versione %1$s.</string>
<string name="logServiceStarting">Avvio del servizio</string>
<string name="serviceStopped">Attività di Automation terminata.</string>
<string name="logServiceStopping">Arresto attività.</string>
<string name="serviceWontStart">Non c\'è nessuna regola. L\'attività non può iniziare.</string>
<string name="serviceStarted">Il servizio di Automation è attivo.</string>
<string name="serviceStopped">Il servizio di Automation è stato fermato.</string>
<string name="serviceWontStart">Nessuna regola definita. Il servizio non può iniziare.</string>
<string name="setScreenBrightness">Impostare la luminosità dello schermo</string>
<string name="setScreenBrightnessEnterValue">Digitare la luminosità desiderata (da 0 a 100).</string>
<string name="settings">Impostazioni</string>
<string name="settingsCategoryHttp">Richieste HTTP(s)</string>
<string name="settingsCategoryNoiseLevelMeasurements">Misura del livello di rumore</string>
<string name="settingsCategoryProcessMonitoring">Controllo di processo</string>
<string name="settingsCategoryProcessMonitoring">Monitoraggio del processo</string>
<string name="settingsErased">Impostazioni cancellate.</string>
<string name="settingsSetToDefault">Impostazioni di default ripristinate.</string>
<string name="settingsWillTakeTime">Per rendere efficaci alcune impostazioni è necessario il riavvio o la modifica delle posizioni.</string>
<string name="showHelp">Descrizione</string>
<string name="settingsReferringToRestrictedFeatures">Le tue impostazioni e/o regole si riferiscono attualmente a funzioni non coperte da una licenza aperta e che pertanto non possono essere fornite nella versione F-Droid. Questo include il rilevamento della tua attuale attività fisica.</string>
<string name="settingsSetToDefault">Impostazioni predefinite ripristinate.</string>
<string name="settingsWillTakeTime">Alcune impostazioni non saranno applicate prima che alcune impostazioni contestuali cambino o che il servizio venga riavviato.</string>
<string name="shareConfigAndLogExplanation">Questo creerà una email con la tua configurazione e i file di log allegati come file zip. Non sarà inviata automaticamente, dovrai premere \"invia\". Puoi anche cambiare il destinatario con te stesso, per esempio.</string>
<string name="shareConfigAndLogFilesWithDev">Condividere i file di configurazione e di registro con lo sviluppatore (via e-mail).</string>
<string name="showHelp">Mostra Aiuto</string>
<string name="showIcon">Mostra icona</string>
<string name="showIconWhenServiceIsRunning">Mostra una icona quando attiva (nascondere funziona solo sotto Android 7)</string>
<string name="showIconWhenServiceIsRunning">Mostra una icona quando il servizio è attivo (nasconderla funziona solo in versioni inferiori ad Android 7)</string>
<string name="showOnMap">Mostra sulla mappa</string>
<string name="someOptionsNotAvailableYet">Alcune opzioni sono disabilitate in quanto non ancora implementate. Saranno introdotte in una versione successiva.</string>
<string name="soundMode">Sonoro</string>
<string name="soundMode">Modalità sonora</string>
<string name="soundModeNormal">Normale</string>
<string name="soundModeSilent">Silenziato</string>
<string name="soundModeSilent">Silenziosa</string>
<string name="soundModeVibrate">Vibrazione</string>
<string name="soundSettings">Impostazioni del sonoro</string>
<string name="soundSettings">Impostazioni del suono</string>
<string name="speedMaximumTime">Tempo in minuti</string>
<string name="speedMaximumTimeBetweenLocations">Tempo massimo tra la rilevazione di due posizioni per determinare la velocità.</string>
<string name="speedMaximumTimeBetweenLocations">Tempo massimo tra la rilevazione di due posizioni per determinarne la velocità.</string>
<string name="start">Inizio</string>
<string name="startAtSystemBoot">Avvio automatic al boot</string>
<string name="startNewThreadForRuleExecution">Nuovo tentativo di attivazione della regola.</string>
<string name="startAppByAction">per azione</string>
<string name="startAppByActivity">per attività</string>
<string name="startAppBySendBroadcast">per sendBroadcast()</string>
<string name="startAppByStartActivity">per startActivity()</string>
<string name="startAppChoiceNote">Qui hai 2 opzioni generali: 1. Puoi avviare un programma selezionando un\'attività. Immagina questo come la preselezione di una specifica schermata/finestra di un\'applicazione. Tieni a mente che questo potrebbe non funzionare sempre. Questo perché le finestre di un\'applicazione potrebbero interagire tra loro, ad esempio per passarsi dei parametri. Quando si avvia direttamente una schermata specifica la cui interazione non è ancora avvenuta, la finestra potrebbe chiudersi istantaneamente (quindi non verrà mai mostrata). Ma puoi provare comunque! Inserisci un percorso di attività manualmente, ma si raccomanda di usare il pulsante \"Seleziona\". Se decidi di inserirlo manualmente, digita il nome del pacchetto dell\'applicazione nel campo superiore e il percorso completo dell\'attività in quello inferiore. 2. Selezione per azione. Invece che selezionare una specifica finestra puoi anche avviare un programma per mezzo di un\'azione. Questo è come gridare "Vorrei xyz" e se c\'è un\'applicazione installata che è registrata con quella funzione, verrà avviata. Un buon esempio sarebbe avviare un navigatore - potresti anche averne più di uno installato (ma uno è di solito quello di default). Devi inserire questo manualmente, mentre PackageName è opzionale qui. Tieni a mente che nessuna variabile sarà risolta. Se vuoi avviare la fotocamera per esempio usando \"MediaStore.ACTION_IMAGE_CAPTURE\" non funzionerà. Devi dare un\'occhiata alla documentazione di Android e usare invece il valore effettivo di questa variabile che in questo esempio sarebbe \"android.media.action.IMAGE_CAPTURE\".</string>
<string name="startAppSelectionType">Metodo per\nselezionare l\'applicazione</string>
<string name="startAppStartType">Seleziona tipo di avvio</string>
<string name="startAtSystemBoot">Avvio automatico al boot</string>
<string name="startAutomationAsService">Avvia Automation come un servizio</string>
<string name="startNewThreadForRuleExecution">Iniziata nuova esecuzione per l\'attivazione della regola.</string>
<string name="startOtherActivity">Inizia una nuova app</string>
<string name="startServiceAfterAppUpdate">Riavvia automaticamente l\'app dopo un aggiornamento se era già in esecuzione.</string>
<string name="startServiceAfterAppUpdateShort">Riavvio dopo aggiornamento</string>
<string name="started">avviando</string>
<string name="starting">avvio</string>
<string name="startingGpsTimeout">In attesa del GPS</string>
<string name="startingPeriodicProcessMonitoringEngine">Avviare il processo ciclico di monitoraggio.</string>
<string name="logStartingServiceAfterAppUpdate">Inizio attività dopo l\'aggiornamento.</string>
<string name="logStartingServiceAtPhoneBoot">Starting service at phone boot.</string>
<string name="status">Riepilogo</string>
<string name="stillGettingPosition">In attesa</string>
<string name="stopped">terminando</string>
<string name="stopping">quando finisce</string>
<string name="stoppingPeriodicProcessMonitoringEngine">Fermare il processo ciclico di monitoraggio engine.</string>
<string name="storeSettings">Scrivere e/o leggere le impostazioni</string>
<string name="startScreen">Schermo di Avvio</string>
<string name="startScreenSummary">Seleziona la schermata con cui le applicazioni si aprono all\'inizio.</string>
<string name="startServiceAfterAppUpdate">Riavvia automaticamente il servizio se era già in esecuzione, dopo che l\'applicazione venga aggiornata.</string>
<string name="startServiceAfterAppUpdateShort">Riavvio del servizio dopo l\'aggiornamento</string>
<string name="started">avviato</string>
<string name="starting">avviando</string>
<string name="startingGpsTimeout">Avviando timeout GPS</string>
<string name="status">Stato</string>
<string name="stillGettingPosition">Ancora in attesa della posizione</string>
<string name="stopped">terminatoo</string>
<string name="stopping">terminando</string>
<string name="storeSettings">Leggere e scrivere le impostazioni</string>
<string name="stringNotAllowed">La stringa %1$s non è permessa.</string>
<string name="sunday">Domenica</string>
<string name="systemSettingsNote1">E\' necessaria labilitazione a modificare alcune impostazioni del sistema operativo (anche cose semplici come accendere il Bluetooth o ilm Wifi). Dopo aver selezionato "Continua" si aprirà un popup per abilitare Automation. Premi il tasto "back" per tornare.</string>
<string name="systemSettingsNote1">Il permesso di cambiare alcune impostazioni del sistema operativo è necessario (anche per cose semplici come attivare il bluetooth o il wifi). Dopo aver cliccato su \"continua\" si aprirà una finestra dove dovrai abilitare questo per Automation. Poi premi il tasto \"indietro\".</string>
<string name="systemSettingsNote2">Ulteriori autorizzazioni verranno richieste in una seconda finestra.</string>
<string name="textToSpeak">Text to speak</string>
<string name="text">Testo</string>
<string name="textMessageAnnotations">Puoi inserire direttamente un numero di telefono. In alternativa usa l\'opzione contatti per sceglierne uno. Ma tieni presente che il numero verrà memorizzato qui, non nel contatto. Se cambi il numero di telefono di un contatto, dovrai aggiornarlo nella questa regola. Non si aggiorna da solo.</string>
<string name="textToSend">Testo da inviare</string>
<string name="textToSpeak">Testo da leggere</string>
<string name="textTooShort">Il testo deve avere almeno 10 caratteri.</string>
<string name="theFollowingPermissionsHaveBeenDenied">Sono state negate le seguenti autorizzazioni:</string>
<string name="theseAreThePermissionsRequired">Necessitano le seguenti autorizzazioni:</string>
<string name="theseAreThePermissionsRequired">Queste sono le autorizzazioni necessarie:</string>
<string name="thursday">Giovedì</string>
<string name="timeBetweenNoiseLevelMeasurementsSummary">Secondi tra misure di livello di rumore</string>
<string name="timeBetweenNoiseLevelMeasurementsTitle">Secondi tra misure di livello di rumore</string>
<string name="timeBetweenProcessMonitoringsSummary">Più è basso e più alto sarà il consume della batteria</string>
<string name="timeBetweenProcessMonitoringsSummary">Più è basso e più sarà alto il consumo della batteria</string>
<string name="timeBetweenProcessMonitoringsTitle">Secondi tra un monitoraggio e l\'altro</string>
<string name="timeForUpdate">Intervallo di aggiornamento [millisecondi]</string>
<string name="timeFrameWhichDays">Che giorno?</string>
<string name="timeFrameWhichDays">In che giorni?</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>
<string name="toggleRule">Reversibile</string>
<string name="toggling">Inversione</string>
<string name="triggerCharging">Carica della Batteria</string>
<string name="triggerHeadsetPlugged">Inserimento auricolari</string>
<string name="timeoutForGpsComparisonsSummary">Massimo tempo in secondi per cercare di individuare la posizione GPS per la comparazione. Allo scadere sarà assunta valida l\'ultima localizzazione rilevata.</string>
<string name="timeoutForGpsComparisonsTitle">Timeout del GPS [sec]</string>
<string name="title">Titolo</string>
<string name="to">a</string>
<string name="toggableRules">Regole \"Reversibili\"</string>
<string name="toggle">reversibile</string>
<string name="toggleNotAllowed">La reversibilità al momento è disponibile solo per le regole che hanno come evento un tag NFC. Consulta l\'aiuto per i dettagli.</string>
<string name="toggleRule">Regola Reversibile</string>
<string name="toggling">Attivando</string>
<string name="triggerCharging">Batteria sotto carica</string>
<string name="triggerHeadsetPlugged">Connessione Auricolari</string>
<string name="triggerNoiseLevel">Livello del rumore di fondo</string>
<string name="triggerOnlyAvailableIfPlayServicesInstalled">Questa evento è valida solo se Google play service è installato.</string>
<string name="triggerOnlyAvailableIfPlayServicesInstalled">Questa evento è valido solo se Google play service è installato.</string>
<string name="triggerPointOfInterest">Posizione</string>
<string name="triggerSpeed">Velocità</string>
<string name="triggerTimeFrame">Intervallo</string>
<string name="triggerUrlReplacementPositionError">Hai chiesto di aggiungere una posizione alla tua URL. Purtroppo non ho ancora alcuna posizione.</string>
<string name="triggerUrlReplacementPositionError">Hai chiesto di aggiungere una posizione alla tua URL. Purtroppo non ho ancora ricevuto nessuna posizione.</string>
<string name="triggerUsb_host_connection">connessione al computer (USB)</string>
<string name="triggers">evento(i)</string>
<string name="triggersComment">(le attive saranno applicate in AND)</string>
<string name="triggers">Evento(i)</string>
<string name="triggersComment">(tutti gli eventi devono essere validi allo stesso tempo)</string>
<string name="tuesday">Martedì</string>
<string name="logUnboundFromService">Servizio disimpegnato.</string>
<string name="unknownActionSpecified">Azione non riconosciuta.</string>
<string name="unknownError">Errore indeterminato.</string>
<string name="until">finchè</string>
<string name="urlLegend">Variabili:\n È possibile utilizzare le seguenti variabili. All\'innesco saranno sostituite con il valore corrispondente sul dispositivo. Includi le parentesi nella tuo testo.\n\n[uniqueid] - L\'imei del tuo dispositivo\n[serialnr] - Il serial number del tuo dispositivio\n[latitude] - La latitudine del tuo dispositivo\n[longitude] - La longitudine del tuo dispositivo\n[phonenr] - Nr ultima chiamata (entrante o uscente)\n[d] - Il giorno del mese, sempre 2 cifre\n[m] - Mese in formato numerico, sempre 2 cifre\n[Y] - Lanno, sempre 4 cifre\n[h] - Ore in formato 12 ore, sempre 2 cifre con due punti\n[H] - Ore in formato 24 ore, sempre 2 cifre con due punti\n[i] - Minuti, sempre 2 cifre\n[s] - Secondi, sempre 2 cifre\n[ms] - millisecondi, sempre 3 cifre
</string>
<string name="urlLegend">Variabili:\n È possibile utilizzare le seguenti variabili. All\'attivazione saranno sostituite con il valore corrispondente sul dispositivo. Includi le parentesi nel tuo testo.\n\n[uniqueid] - Il numero di serie del tuo dispositivo\n[serialnr] - Il serial number del tuo dispositivio\n[latitude] - La latitudine del tuo dispositivo\n[longitude] - La longitudine del tuo dispositivo\n[phonenr] - Numero dell\'ultima chiamata (entrante o uscente)\n[d] - Il giorno del mese, sempre 2 cifre\n[m] - Mese in formato numerico, sempre 2 cifre\n[Y] - L\anno, sempre 4 cifre\n[h] - Ore in formato 12 ore, sempre 2 cifre con due punti\n[H] - Ore in formato 24 ore, sempre 2 cifre con due punti\n[i] - Minuti, sempre 2 cifre\n[s] - Secondi, sempre 2 cifre\n[ms] - millisecondi, sempre 3 cifre [notificationTitle] - titolo dell\'ultima notifica [notificationText] - testo dell\'ultima notifica</string>
<string name="urlToTrigger">URL da caricare:</string>
<string name="urlTooShort">La url deve avere almeno 10 caratteri.</string>
<string name="usbTetheringFailForAboveGingerbread">Questo molto probabilmente non funziona con versioni superiori ad Android 2.3. Tuttavia è possibile utilizzare la connessione wifi tethering per attivare la regola.</string>
<string name="useAuthentication">Attiva Username e Password</string>
<string name="urlTooShort">L\'url deve avere almeno 10 caratteri.</string>
<string name="usbTetheringFailForAboveGingerbread">Questo molto probabilmente non funzionerà dato che sei su una versione superiore ad Android 2.3. Tuttavia è possibile utilizzare la connessione wifi tethering per attivare la regola se sei su una versione inferiore ad Android 7.</string>
<string name="useAuthentication">Usa l\'autenticazione</string>
<string name="useExistingTag">Utilizzo di un tag NFC esistente</string>
<string name="useTextToSpeechOnNormalSummary">Usa TextToSpeech nel modo normale</string>
<string name="useTextToSpeechOnNormalTitle">TTS in modo normale</string>
@ -533,19 +577,21 @@ Selezionare su “Continua” quando si è pronti a procedere.</string>
<string name="useTextToSpeechOnSilentTitle">TTS in modo silenzioso</string>
<string name="useTextToSpeechOnVibrateSummary">Usa TextToSpeech nel modo vibrazione</string>
<string name="useTextToSpeechOnVibrateTitle">TTS in modo vibrazione</string>
<string name="username">Username</string>
<string name="usingNewThreadForRuleExecution">Nuovo tentativo di avvio della regola.</string>
<string name="username">Nome utente</string>
<string name="usingNewThreadForRuleExecution">Iniziata nuova esecuzione per l\'attivazione della regola.</string>
<string name="version">Versione %1$s.</string>
<string name="vibrateWhenRinging">Vibrazione alla chiamata</string>
<string name="volumeAlarms">Allarmi sonori</string>
<string name="volumeMusicVideoGameMedia">Multimedia (musica, video …)</string>
<string name="volumeRingtoneNotifications">Toni e notifiche</string>
<string name="volumeTest">Taratura audio</string>
<string name="volumeTesterExplanation">Per calcolare il valore del rumore di fondo in dB è necessario specificare un valore di riferimento fisico (si prega di leggere Wikipedia per ulteriori informazioni). Questo valore è probabilmente diverso per ogni telefono. Trascinare il cursore per modificare il valore di riferimento fisico definito. Più alto è il valore di riferimento e più basso sarà il valore misurato in dB. Misurazioni costanti saranno effettuati ogni 3 secondi ed i risultati visualizzati sotto. Premere indietro quando si è trovato un valore adeguato.</string>
<string name="volumes">Livelli sonori</string>
<string name="volumeTesterExplanation">Per calcolare il valore del rumore di fondo in dB è necessario specificare un valore di riferimento fisico (si prega di leggere Wikipedia per ulteriori informazioni). Questo valore è probabilmente diverso per ogni telefono. Trascinare il cursore per modificare il valore di riferimento fisico definito. Più alto è il valore di riferimento e più basso sarà il valore misurato in dB. Misurazioni costanti saranno effettuate ogni %1$s secondi ed i risultati visualizzati sotto. Premere indietro quando hai trovato un valore adeguato.</string>
<string name="volumes">Volumi</string>
<string name="waitBeforeNextAction">Attesa prima della azione successiva</string>
<string name="waitBeforeNextActionEnterValue">Inserisci il valore della pausa tra tra un\'azione e la successiva (millisecondi).</string>
<string name="wakeupDevice">Sveglia del dispositivo</string>
<string name="wakeupDeviceValue">Inserisci per quanto tempo il dispositivo deve rimanere attivo (millisecondi). In assenza si assume 0.</string>
<string name="wakeupDevice">Sveglia il dispositivo</string>
<string name="wakeupDeviceValue">Inserisci per quanto tempo il dispositivo deve rimanere attivo (millisecondi). Usa 0 per i valori standard.</string>
<string name="warning">Attenzione</string>
<string name="wednesday">Mercoledì</string>
<string name="whatToDoWithAction">Cosa vuoi fare?</string>
<string name="whatToDoWithIntentPair">Cosa fare con la coppia?</string>
@ -557,13 +603,12 @@ Selezionare su “Continua” quando si è pronti a procedere.</string>
<string name="wifi">wifi</string>
<string name="wifiConnection">Connessione wifi</string>
<string name="wifiName">Nome wifi</string>
<string name="wifiNameMatchesRuleWillApply">Il nome della wifi combacia. La regola può è valida.</string>
<string name="wifiNameMatchesRuleWillApply">Il nome della wifi combacia. La regola si applicherà.</string>
<string name="wifiNameSpecifiedCheckingThat">Verifica della wifi indicata.</string>
<string name="wifiState">Stato Wifi</string>
<string name="wifiTetheringFailForAboveNougat">Questo non funziona più dato che sei su una versione superiore ad Android 7.</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="writingSettingsToPersistentMemory">Scrivo le impostazioni nella memoria di massa.</string>
<string name="writeLogFile">Memorizza un file di log</string>
<string name="yes">Si</string>
<string name="edit">Elaborare</string>
</resources>
</resources>

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="app_name" translatable="false">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>
@ -16,28 +14,27 @@
<string name="serviceWontStart">No rules defined. Service won\'t start.</string>
<string name="serviceStarted">Automation Service started.</string>
<string name="version">Version %1$s.</string>
<string name="logServiceStarting">Starting service.</string>
<string name="logNotAllMeasurings">Don\'t have all location measurings, yet. Can\'t do comparison.</string>
<string name="distanceBetween">Distance between GPS location and network location is</string>
<string name="radiusSuggestion">meters. This +1 should be the absolute minimum radius.</string>
<string name="logServiceStarting" translatable="false">Starting service.</string>
<string name="logNotAllMeasurings" translatable="false">Don\'t have all location measurings, yet. Can\'t do comparison.</string>
<string name="distanceBetween">Distance between GPS location and network location is %1$d meters. This +1m should be the absolute minimum radius.</string>
<string name="comparing">Have both network and gps location. Comparing...</string>
<string name="logNoSuitableProvider">No suitable location providers could be used.</string>
<string name="logNoSuitableProvider" translatable="false">No suitable location providers could be used.</string>
<string name="positioningWindowNotice">If you are in a building it is strongly advised to place your device next to a window until a position has been found. Otherwise it may take a very long time if it is able to find one at all.</string>
<string name="gettingPosition">Getting position. Please wait...</string>
<string name="logGettingPositionWithProvider">Requesting location using provider:</string>
<string name="logGettingPositionWithProvider" translatable="false">Requesting location using provider:</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="logGotGpsUpdate">Got GPS update. Accuracy:</string>
<string name="logGotNetworkUpdate">Got network update. Accuracy:</string>
<string name="logGotGpsUpdate" translatable="false">Got GPS update. Accuracy:</string>
<string name="logGotNetworkUpdate" translatable="false">Got network update. Accuracy:</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="logAttemptingToBindToService" translatable="false">Attempting to bind to service... </string>
<string name="logAttemptingToUnbindFromService" translatable="false">Attempting to unbind from service... </string>
<string name="logBoundToService" translatable="false">Bound to service.</string>
<string name="logUnboundFromService" translatable="false">Unbound from service.</string>
<string name="logServiceAlreadyRunning" translatable="false">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>
@ -73,7 +70,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 +88,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>
@ -123,14 +120,14 @@
<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="minimumTimeForLocationUpdates">Minimum time change in milliseconds 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="helpTextTimeFrame">If you specify a rule with a timeframe you have two choices. You can choose between entering OR leaving a timeframe. Either way a rule is triggered only once. So 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="helpTextSound">On the main screen you can use lock sound changes to temporarily avoid rule based sound changes. E.g. you may be in a situation or place where usually ringtones are ok, but this one time it would be disturbing. The feature will automatically deactivate once the configured time has elapsed. Click the + button to add the given amount of time. Once it is active you may deactivate it again using the toggle button (and that way enable rule based sound changes again).</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>
@ -190,38 +187,38 @@
<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="unknownActionSpecified" translatable="false">Unknown action specified</string>
<string name="logErrorTriggeringUrl" translatable="false">Error triggering URL</string>
<string name="errorChangingScreenRotation" translatable="false">Error changing screen rotation</string>
<string name="errorDeterminingWifiApState" translatable="false">Error determining wifiAp state</string>
<string name="errorActivatingWifiAp" translatable="false">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="logAttemptingDownloadOf" translatable="false">attempting download of</string>
<string name="logErrorGettingConnectionManagerService" translatable="false">Error getting connectionManager service. Not doing anything to UsbTethering.</string>
<string name="logErrorDeterminingCurrentUsbTetheringState" translatable="false">Error determining current UsbTethering state.</string>
<string name="logDetectingTetherableUsbInterface" translatable="false">Detecting tetherable usb interface.</string>
<string name="logClearingBothLocationListeners" translatable="false">Clearing both location listeners.</string>
<string name="logStartingServiceAfterAppUpdate" translatable="false">Starting service after app update.</string>
<string name="logNotStartingServiceAfterAppUpdate" translatable="false">Not starting service after app update.</string>
<string name="logStartingServiceAtPhoneBoot" translatable="false">Starting service at phone boot.</string>
<string name="logNotStartingServiceAtPhoneBoot" translatable="false">Not starting service at phone boot.</string>
<string name="applicationHasBeenUpdated" translatable="false">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="ruleDoesntApplyWeAreSlowerThan" translatable="false">Rule doesn\'t apply. We are slower than</string>
<string name="ruleDoesntApplyWeAreFasterThan" translatable="false">Rule doesn\'t apply. We are faster than</string>
<string name="ruleDoesntApplyItsQuieterThan" translatable="false">Rule doesn\'t apply. It\'s quieter than</string>
<string name="ruleDoesntApplyItsLouderThan" translatable="false">Rule doesn\'t apply. It\'s louder than</string>
<string name="ruleDoesntApplyBatteryLowerThan" translatable="false">Rule doesn\'t apply. Battery level is lower than</string>
<string name="ruleDoesntApplyBatteryHigherThan" translatable="false">Rule doesn\'t apply. Battery level is higher than</string>
<string name="ruleDoesntApplyNotTheCorrectSsid" translatable="false">Rule doesn\'t apply. Not the correct SSID (demanded: \"%1$s\", given: \"%2$s\").</string>
<string name="ruleDoesntApplyNoTagLabel" translatable="false">Rule doesn\'t apply. There is no tag label or not tag at all.</string>
<string name="ruleDoesntApplyWrongTagLabel" translatable="false">Rule doesn\'t apply. Wrong tag label.</string>
<string name="ruleIsDeactivatedCantApply" translatable="false">Rule %1$s is deactivated, can\'t apply.</string>
<string name="starting">starting</string>
<string name="stopping">stopping</string>
<string name="connecting">connecting</string>
@ -244,12 +241,12 @@
<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="gpsComparisonTimeoutStop" translatable="false">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="startingGpsTimeout" translatable="false">Starting GPS timeout.</string>
<string name="forcedLocationUpdate" translatable="false">Forced location update</string>
<string name="forcedLocationUpdateLong" translatable="false">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>
@ -265,35 +262,35 @@
<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="refreshingProcessList" translatable="false">Refreshing process list.</string>
<string name="processes">Processes</string>
<string name="startingPeriodicProcessMonitoringEngine">Starting periodic process monitoring engine.</string>
<string name="startingPeriodicProcessMonitoringEngine" translatable="false">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="periodicProcessMonitoringIsAlreadyRunning" translatable="false">Periodic process monitoring is already running. Won\'t start it again.</string>
<string name="stoppingPeriodicProcessMonitoringEngine" translatable="false">Stopping periodic process monitoring engine.</string>
<string name="periodicProcessMonitoringIsNotActive" translatable="false">Periodic process monitoring is not active. Can\'t stop it.</string>
<string name="periodicProcessMonitoringStarted" translatable="false">Periodic process monitoring started.</string>
<string name="periodicProcessMonitoringStopped" translatable="false">Periodic process monitoring stopped.</string>
<string name="rearmingProcessMonitoringMessage" translatable="false">Rearming process monitoring message.</string>
<string name="notRearmingProcessMonitoringMessageStopRequested" translatable="false">Not rearming process monitoring message, stop requested.</string>
<string name="messageReceivedStatingProcessMonitoringIsComplete" translatable="false">Message received stating process monitoring is complete.</string>
<string name="appStarted" translatable="false">App started.</string>
<string name="appStopped" translatable="false">App stopped.</string>
<string name="runningApp" translatable="false">Running app</string>
<string name="errorWritingSettingsToPersistentMemory" translatable="false">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="writingSettingsToPersistentMemory" translatable="false">Writing settings to persistent memory.</string>
<string name="refreshingSettingsFromFileToMemory" translatable="false">Refreshing settings from file to memory.</string>
<string name="errorReadingSettings" translatable="false">Error reading settings.</string>
<string name="invalidStuffStoredInSettingsErasing" translatable="false">Invalid stuff stored in settings. Erasing settings...</string>
<string name="initializingSettingsToPersistentMemory" translatable="false">Initializing settings to persistent memory.</string>
<string name="errorInitializingSettingsToPersistentMemory" translatable="false">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="applyingSettingsAndRules" translatable="false">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>
@ -305,15 +302,15 @@
<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="wifiNameSpecifiedCheckingThat" translatable="false">Wifi name specified, checking that.</string>
<string name="wifiNameMatchesRuleWillApply" translatable="false">Wifi name matches. Rule will apply.</string>
<string name="noWifiNameSpecifiedAnyWillDo" translatable="false">No wifi name specified, any will do.</string>
<string name="ruleCheckOf" translatable="false">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="triggerUrlReplacementPositionError" translatable="false">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>
@ -362,11 +359,11 @@
<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="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="cantDownloadTooFewRequestsInSettings" translatable="false">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>
@ -390,8 +387,8 @@
<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="poiCouldBeInRange" translatable="false">At least location %1$s could be in range, if not others in addition.</string>
<string name="noPoiInRelevantRange" translatable="false">No location in relevant range.</string>
<string name="activityDetection">Activity detection</string>
<string name="android.permission.ACTIVITY_RECOGNITION">Activity detection</string>
<string name="detectedActivity">Detected activity:</string>
@ -404,8 +401,8 @@
<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="ruleDoesntApplyActivityGivenButTooLowProbability" translatable="false">Rule doesn\'t apply. Detected activity %1$s given, but too low probability (%2$s %%), required %3$s %%.</string>
<string name="ruleDoesntApplyActivityNotPresent" translatable="false">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>
@ -413,7 +410,7 @@
<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="outgoingCallTo">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>
@ -445,27 +442,26 @@
<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="ruleDoesntApplyWrongHeadphoneType" translatable="false">Rule doesn\'t apply. Wrong headphone type.</string>
<string name="ignoringActivityDetectionUpdateTooSoon" translatable="false">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="atLeastRuleXisUsingY" translatable="false">At least rule \"%1$s\" is using a trigger of type \"%2$s\".</string>
<string name="privacyLocationingTitle">Only private locationing</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="enforcingGps" translatable="false">Private Locationing enabled, enforcing GPS use.</string>
<string name="notEnforcingGps" translatable="false">Private Locationing not enabled, using regular provider search.</string>
<string name="gpsMeasurement" translatable="false">GPS measurement</string>
<string name="gpsMeasurementTimeout" translatable="false">GPS measurement stopped due to timeout.</string>
<string name="cellMastChanged" translatable="false">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 CellLocationChangedReceiver.</string>
<string name="poiHasNoWifiNotStoppingCellLocationListener">Location doesn\'t have wifi. Not stopping CellLocationChangedReceiver.</string>
<string name="poiHasWifiStoppingCellLocationListener" translatable="false">Location has wifi. Stopping CellLocationChangedReceiver.</string>
<string name="poiHasNoWifiNotStoppingCellLocationListener" translatable="false">Location doesn\'t have wifi. Not stopping CellLocationChangedReceiver.</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>
@ -492,7 +488,8 @@
<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="usbTetheringFailForAboveGingerbread">This will most likely not work as you\'re above Android 2.3. If you\'re below Android 7 you could use wifi tethering instead instead.</string>
<string name="wifiTetheringFailForAboveNougat">This will not work anymore as you\'re above Android 7.</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>
@ -507,10 +504,10 @@
<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="phoneIsRooted" translatable="false">Phone is rooted.</string>
<string name="phoneIsNotRooted" translatable="false">Phone is not rooted.</string>
<string name="dataConWithRootSuccess" translatable="false">Data connection was successfully changed using superuser permissions.</string>
<string name="dataConWithRootFail" translatable="false">Data could not be changed using superuser permissions.</string>
<string name="rootExplanation">You need to root your phone for this function to work. Afterwards you needs to \"run the rule manually\" to show up the superuser permission question. When the superuser popups shows up you need to always allow the application to do that. Otherwise the rule cannot function when the phone is unattended.</string>
<string name="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>
@ -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>
@ -579,7 +576,7 @@
<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">Click and hold an item for options.</string>
<string name="ruleActivationComplete">Rule \"%1$s\" finished.</string>
<string name="ruleActivationComplete" translatable="false">Rule \"%1$s\" finished.</string>
<string name="positioningEngine">Positioning engine</string>
<string name="googleSarcasm">Thanks to Google\'s infinite whisdom and constant endeavor to protect everyone\'s privacy rules that may send sms or involve the users phone state have to be stripped off applicable triggers and actions.</string>
<string name="startAutomationAsService">Start automation as a service</string>
@ -592,16 +589,90 @@
<string name="autoBrightnessNotice">If you use auto brightness the brightness value you use below will probably not be used long.</string>
<string name="screenLockSoundNotice">Screenlock sounds cannot automatically be changed anymore on devices running Android version 6.0 or higher. Whatever you set here, it will not work in either direction.</string>
<string name="startScreen">Start screen</string>
<string name="startScreenSummary">Select the screen the applications opens withs at start.</string>
<string name="startScreenSummary">Select the screen the applications opens with at start.</string>
<string name="executeRulesAndProfilesWithSingleClickTitle">Run rules/profiles with single click.</string>
<string name="googleLocationChicanery">This app collects location data to determine if you\'re currently at one of the locations you created. Furthermore it is used to determine your current speed if you are using that trigger in rules. That is done even when the app is closed or not in use (but only when the service is activated).</string>
<string name="googleLocationChicanery">This app collects location data to enable location based rules and speed detection even when the app is closed or not in use.</string>
<string name="googleLocationChicaneryOld">This app collects location data to determine if you\'re currently at one of the locations you created. Furthermore it is used to determine your current speed if you are using that trigger in rules. That is done even when the app is closed or not in use (but only when the service is activated).</string>
<string name="android.permission.ACCESS_BACKGROUND_LOCATION">Read location in background.</string>
<string name="deviceDoesNotHaveBluetooth">This device does not seem to have bluetooth. You can still continue configuring this, but it will most likely not have an effect.</string>
<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. 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 in the background 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. In addition you may use regular expressions. To test a regular expression I like this page:</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="warning">Warning</string>
<string name="ringing">ringing</string>
<string name="from">from</string>
<string name="to">to</string>
<string name="matching">matching</string>
<string name="urlRegex" translatable="false">https://regex101.com/</string>
<string name="loadWifiList">Load wifi list</string>
<string name="needLocationPermForWifiList">The list of wifis your device has been connected to could be used to determine which places you have been to. That\'s why the location permission is required to load the list of wifis. If you want to be able to pick one from the list you need to grant that permission. If not you can still enter your wifi name manually.</string>
<string name="noKnownWifis">There are no known wifis on your device.</string>
<string name="urlToTriggerExplanation">This feature does NOT open a browser, but triggers a URL in the background. You can use this e.g. to send commands to your home automation.</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.1'
// 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

Some files were not shown because too many files have changed in this diff Show More