89 Commits

Author SHA1 Message Date
6593f6c923 Compile fix 2021-08-14 01:53:48 +02:00
7415830dd7 New release 2021-07-29 14:14:20 +02:00
dce68d79bd New release 2021-07-29 14:05:57 +02:00
397dadd8c7 Translation 2021-07-28 11:49:13 +02:00
c51c46707b Translation 2021-07-21 13:06:42 +02:00
d699285b5a Translation 2021-07-16 18:33:25 +02:00
cf3db22ffb Translation 2021-07-08 22:41:58 +02:00
f53abe2b23 TabLayout 2021-07-08 17:57:33 +02:00
ba2f96713d TabLayout 2021-07-07 21:01:43 +02:00
21d9351b2b Translations 2021-07-07 12:52:26 +02:00
a8bfc6f7ec Vibrate pattern 2021-07-04 15:53:24 +02:00
71d9791603 Vibrate pattern 2021-07-04 02:27:07 +02:00
d71177f3da Vibrate pattern 2021-07-04 02:14:26 +02:00
eef6c3234a Speed calculation 2021-07-03 02:42:24 +02:00
192142c76b Translation 2021-07-02 10:46:25 +02:00
9a2a0aa6a4 Encryption 2021-06-29 23:10:51 +02:00
c18a880ad7 Logging 2021-06-28 23:28:46 +02:00
a845ea7c63 v1.6.35 2021-06-24 22:38:12 +02:00
2d9695344b Permission 2021-06-23 22:01:40 +02:00
24d05e6d87 Location 2021-06-23 20:20:38 +02:00
07b0513eae Permission 2021-06-20 23:45:02 +02:00
e445b787a9 Fixed cache problem after rule clone 2021-06-20 22:24:17 +02:00
82156059fa Translations 2021-06-16 23:12:00 +02:00
b976ff95b6 Translations 2021-06-16 21:45:29 +02:00
a4b2745b7b Layout changes 2021-06-13 13:53:14 +02:00
844be6a4a7 Manage POI 2021-06-12 18:56:03 +02:00
49a48fc371 Manage POI 2021-06-12 14:02:10 +02:00
722750b724 Translations. 2021-06-12 02:27:51 +02:00
ab51eb3655 POI management changes 2021-06-10 23:22:38 +02:00
7b88e7a612 Automatic update check for APK version. 2021-06-09 22:41:47 +02:00
bb2f5c9842 Translations. 2021-06-09 19:59:31 +02:00
883a7e8341 Translations. 2021-06-09 17:02:27 +02:00
bfc0e3ac4f Automatic update check for APK version. 2021-06-08 23:14:09 +02:00
23ded3a851 Minor corrections. 2021-06-08 20:04:02 +02:00
6d363fd02d Translations 2021-06-07 22:52:38 +02:00
3c0f889db7 Minor corrections. 2021-06-07 20:02:42 +02:00
d018c27f0e Minor corrections. 2021-06-05 12:11:05 +02:00
7e36f0f32e New release 2021-06-02 22:36:39 +02:00
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
122 changed files with 9205 additions and 3916 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>

View File

@ -11,8 +11,8 @@ android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
useLibrary 'org.apache.http.legacy'
versionCode 102
versionName "1.6.30"
versionCode 108
versionName "1.6.38"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -40,7 +40,6 @@ android {
googlePlayFlavor
{
dimension "version"
// applicationIdSuffix ".googlePlay"
versionNameSuffix "-googlePlay"
targetSdkVersion 29
}
@ -48,15 +47,12 @@ android {
fdroidFlavor
{
dimension "version"
// applicationIdSuffix ".fdroid"
// versionNameSuffix "-fdroid"
targetSdkVersion 28
}
apkFlavor
{
dimension "version"
// applicationIdSuffix ".apk"
versionNameSuffix "-apk"
targetSdkVersion 28
}
@ -64,13 +60,18 @@ android {
}
dependencies {
googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0'
googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0'
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0'
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.linkedin.dexmaker:dexmaker:2.25.0'
implementation 'org.apache.commons:commons-lang3:3.0'
//implementation "androidx.security:security-crypto:1.0.0"
//implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'

View File

@ -5,13 +5,13 @@
"kind": "Directory"
},
"applicationId": "com.jens.automation2",
"variantName": "processGooglePlayFlavorReleaseResources",
"variantName": "googlePlayFlavorRelease",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 102,
"versionName": "1.6.30-googlePlay",
"versionCode": 106,
"versionName": "1.6.35-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" />
@ -63,8 +64,7 @@
<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"/>
<!-- Commented out because of Google Play policy -->
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-feature
android:name="android.hardware.telephony"
@ -77,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">
@ -96,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>
@ -142,8 +142,11 @@
<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=".ActivityManageActionVibrate" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
@ -186,27 +189,10 @@
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<service
android:name=".receivers.ActivityDetectionReceiver"
android:exported="false"
android:label="@string/app_name"></service>
<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"/>
<activity android:name=".ActivityManageTriggerNotification"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
@ -218,6 +204,22 @@
</service>
<!-- 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"
android:exported="false"
android:label="@string/app_name"></service>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<service android:name=".location.GeofenceIntentService"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"

View File

@ -7,6 +7,7 @@ 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;
@ -174,6 +175,15 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3);
ruleCollection.add(this);
boolean returnValue = XmlFileInterface.writeFile();
try
{
XmlFileInterface.readFile();
}
catch(Exception e)
{
Miscellaneous.logEvent("w", "Read file", Log.getStackTraceString(e), 3);
}
if(returnValue)
{
@ -217,10 +227,23 @@ public class Rule implements Comparable<Rule>
return XmlFileInterface.writeFile();
}
public boolean cloneRule(Context context)
{
Rule newRule = new Rule();
newRule.setName(this.getName() + " - clone");
newRule.setRuleActive(this.isRuleActive());
newRule.setRuleToggle(this.isRuleToggle());
newRule.setTriggerSet(this.getTriggerSet());
newRule.setActionSet(this.getActionSet());
return newRule.create(context);
}
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;
@ -415,13 +438,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)
)
@ -548,12 +571,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
@ -601,13 +624,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
{
@ -622,7 +661,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))
{
@ -855,7 +897,7 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleIsDeactivatedCantApply), this.getName()), 3);
return false;
}
private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{
boolean wasActivated = false;
@ -916,7 +958,7 @@ public class Rule implements Comparable<Rule>
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle)
if(notLastActive || force || doToggle)
{
String message;
if(!doToggle)
@ -1069,7 +1111,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
@ -1340,10 +1382,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:
@ -1351,7 +1393,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
@ -1359,7 +1402,7 @@ public class Rule implements Comparable<Rule>
}
}
}
return ruleCandidates;
}

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>
@ -135,9 +137,13 @@
<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=".ActivityManageActionVibrate" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
@ -180,6 +186,7 @@
<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" />
@ -194,6 +201,10 @@
</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

View File

@ -7,6 +7,7 @@ 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;
@ -171,6 +172,15 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3);
ruleCollection.add(this);
boolean returnValue = XmlFileInterface.writeFile();
try
{
XmlFileInterface.readFile();
}
catch(Exception e)
{
Miscellaneous.logEvent("w", "Read file", Log.getStackTraceString(e), 3);
}
if(returnValue)
{
@ -214,10 +224,23 @@ public class Rule implements Comparable<Rule>
return XmlFileInterface.writeFile();
}
public boolean cloneRule(Context context)
{
Rule newRule = new Rule();
newRule.setName(this.getName() + " - clone");
newRule.setRuleActive(this.isRuleActive());
newRule.setRuleToggle(this.isRuleToggle());
newRule.setTriggerSet(this.getTriggerSet());
newRule.setActionSet(this.getActionSet());
return newRule.create(context);
}
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;
@ -412,13 +435,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)
)
@ -545,12 +568,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
@ -598,13 +621,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
{
@ -619,7 +658,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))
{
@ -772,6 +814,7 @@ public class Rule implements Comparable<Rule>
}
foundMatch = true;
break;
}
}
@ -840,7 +883,7 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null)
Looper.prepare();
wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null;
@ -870,8 +913,8 @@ public class Rule implements Comparable<Rule>
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
}
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
@ -879,15 +922,15 @@ public class Rule implements Comparable<Rule>
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);
@ -895,7 +938,7 @@ 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++)
{
try
@ -1036,7 +1079,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
@ -1307,10 +1350,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:
@ -1318,7 +1361,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
@ -1326,7 +1370,7 @@ public class Rule implements Comparable<Rule>
}
}
}
return ruleCandidates;
}

View File

@ -61,12 +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"/>
<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">
@ -85,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>
@ -131,8 +133,11 @@
<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=".ActivityManageActionVibrate" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
@ -172,12 +177,14 @@
<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"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
@ -189,6 +196,13 @@
</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"
android:exported="false"
@ -197,12 +211,8 @@
<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"

View File

@ -7,6 +7,7 @@ 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;
@ -63,7 +64,7 @@ public class Rule implements Comparable<Rule>
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle()
{
return ruleToggle;
@ -173,6 +174,15 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3);
ruleCollection.add(this);
boolean returnValue = XmlFileInterface.writeFile();
try
{
XmlFileInterface.readFile();
}
catch(Exception e)
{
Miscellaneous.logEvent("w", "Read file", Log.getStackTraceString(e), 3);
}
if(returnValue)
{
@ -216,10 +226,23 @@ public class Rule implements Comparable<Rule>
return XmlFileInterface.writeFile();
}
public boolean cloneRule(Context context)
{
Rule newRule = new Rule();
newRule.setName(this.getName() + " - clone");
newRule.setRuleActive(this.isRuleActive());
newRule.setRuleToggle(this.isRuleToggle());
newRule.setTriggerSet(this.getTriggerSet());
newRule.setActionSet(this.getActionSet());
return newRule.create(context);
}
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;
@ -414,13 +437,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)
)
@ -547,12 +570,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
@ -600,13 +623,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
{
@ -621,7 +660,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))
{
@ -872,7 +914,7 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null)
Looper.prepare();
wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null;
@ -915,7 +957,7 @@ public class Rule implements Comparable<Rule>
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle)
if(notLastActive || force || doToggle)
{
String message;
if(!doToggle)
@ -1068,7 +1110,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
@ -1339,10 +1381,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:
@ -1350,7 +1392,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
@ -1358,7 +1401,7 @@ public class Rule implements Comparable<Rule>
}
}
}
return ruleCandidates;
}

View File

@ -2,7 +2,6 @@ package com.jens.automation2;
import android.content.Context;
import android.os.AsyncTask;
import android.text.style.TabStopSpan;
import android.util.Log;
import android.widget.Toast;
@ -15,6 +14,8 @@ import java.util.Locale;
public class Action
{
public static final String actionParameter2Split = "ap2split";
public static final String intentPairSeperator = "intPairSplit";
public static final String vibrateSeparator = ",";
public enum Action_Enum {
setWifi,
@ -38,6 +39,7 @@ public class Action
playMusic,
setScreenBrightness,
playSound,
vibrate,
sendTextMessage;
public String getFullName(Context context)
@ -84,6 +86,8 @@ public class Action
return context.getResources().getString(R.string.waitBeforeNextAction);
case wakeupDevice:
return context.getResources().getString(R.string.wakeupDevice);
case vibrate:
return context.getResources().getString(R.string.vibrate);
case setAirplaneMode:
return context.getResources().getString(R.string.airplaneMode);
case setDataConnection:
@ -237,6 +241,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);
@ -341,18 +349,11 @@ public class Action
* 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"))
// Actions.setSound(context, AudioManager.RINGER_MODE_VIBRATE);
// else if(this.getParameter2().equals("normal"))
// Actions.setSound(context, AudioManager.RINGER_MODE_NORMAL);
// else
// {
Profile p = Profile.getByName(this.getParameter2());
if (p != null)
p.activate(context);
// }
break;
case triggerUrl:
triggerUrl(context);
@ -373,7 +374,7 @@ public class Action
Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible);
break;
case startOtherActivity:
Actions.startOtherActivity(getParameter2());
Actions.startOtherActivity(getParameter1(), getParameter2());
break;
case waitBeforeNextAction:
Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2()));
@ -408,6 +409,9 @@ public class Action
case setScreenBrightness:
Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2()));
break;
case vibrate:
Actions.vibrate(getParameter1(), getParameter2());
break;
case playSound:
Actions.playSound(getParameter1(), getParameter2());
break;
@ -455,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);
}
}
@ -486,62 +490,24 @@ public class Action
while(attempts <= Settings.httpAttempts && response.equals("httpError"))
{
Miscellaneous.logEvent("i", "HTTP Request", "Attempt " + String.valueOf(attempts++) + " of " + String.valueOf(Settings.httpAttempts), 3);
// try
// {
// Either thorough checking or no encryption
if(!Settings.httpAcceptAllCertificates | !urlString.toLowerCase(Locale.getDefault()).contains("https"))
// {
// URL url = new URL(urlString);
// URLConnection urlConnection = url.openConnection();
// urlConnection.setReadTimeout(Settings.httpAttemptsTimeout * 1000);
// InputStream in = urlConnection.getInputStream();
// response = Miscellaneous.convertStreamToString(in);
response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword);
// }
else
// {
response = Miscellaneous.downloadURLwithoutCertificateChecking(urlString, urlUsername, urlPassword);
// post = new HttpGet(new URI(urlString));
// final HttpParams httpParams = new BasicHttpParams();
// HttpConnectionParams.setConnectionTimeout(httpParams, Settings.httpAttemptsTimeout * 1000);
// HttpClient client = new DefaultHttpClient(httpParams);
//
// client = sslClient(client);
//
// // Execute HTTP Post Request
// HttpResponse result = client.execute(post);
// response = EntityUtils.toString(result.getEntity());
// }
// }
// catch (URISyntaxException e)
// {
// Miscellaneous.logEvent("w", "HTTP RESULT", Log.getStackTraceString(e), 3);
// }
// catch (ClientProtocolException e)
// {
// Miscellaneous.logEvent("w", "HTTP RESULT", Log.getStackTraceString(e), 3);
// }
// catch (IOException e)
// {
// Miscellaneous.logEvent("w", "HTTP RESULT", Log.getStackTraceString(e), 3);
// e.printStackTrace();
// }
// finally
// {
try
{
Thread.sleep(Settings.httpAttemptGap * 1000);
}
catch (InterruptedException e1)
{
Miscellaneous.logEvent("w", "HTTP RESULT", "Failed to pause between HTTP requests.", 5);
}
// }
// Either thorough checking or no encryption
if(!Settings.httpAcceptAllCertificates || !urlString.toLowerCase(Locale.getDefault()).contains("https"))
response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword);
else
response = Miscellaneous.downloadURLwithoutCertificateChecking(urlString, urlUsername, urlPassword);
try
{
Thread.sleep(Settings.httpAttemptGap * 1000);
}
catch (InterruptedException e1)
{
Miscellaneous.logEvent("w", "HTTP RESULT", "Failed to pause between HTTP requests.", 5);
}
}
// Miscellaneous.logEvent("i", "HTTPS RESULT", response, 3);
Miscellaneous.logEvent("i", "HTTPS RESULT", response, 5);
return response;
}

File diff suppressed because it is too large Load Diff

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;
@ -60,11 +61,11 @@ public class ActivityMainPoi extends ActivityGeneric
}
else
{
if (!ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, ActivityMainPoi.this) || !ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, ActivityMainPoi.this))
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[]{ActivityPermissions.permissionNameLocationCoarse, ActivityPermissions.permissionNameLocationFine});
permissionIntent.putExtra(ActivityPermissions.intentExtraName, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION});
startActivityForResult(permissionIntent, requestCodeForPermission);
}

View File

@ -26,10 +26,14 @@ import java.util.ArrayList;
public class ActivityMainRules extends ActivityGeneric
{
private ListView ruleListView;
ArrayList<Rule> ruleList = new ArrayList<>();
private ArrayAdapter<Rule> ruleListViewAdapter;
public static Rule ruleToEdit;
protected static ActivityMainRules instance = null;
public static final int requestCodeCreateRule = 3000;
public static final int requestCodeChangeRule = 4000;
public static ActivityMainRules getInstance()
{
if(instance == null)
@ -52,21 +56,14 @@ 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;
// }
ruleToEdit = null;
Intent startAddRuleIntent = new Intent(ActivityMainRules.this, ActivityManageRule.class);
startActivityForResult(startAddRuleIntent, 3000);
startActivityForResult(startAddRuleIntent, requestCodeCreateRule);
}
});
ruleListViewAdapter = new RuleArrayAdapter(this, R.layout.view_for_rule_listview, ruleList);
ruleListView = (ListView)findViewById(R.id.lvRuleList);
ruleListViewAdapter = new RuleArrayAdapter(this, R.layout.view_for_rule_listview, Rule.getRuleCollection());
ruleListView.setClickable(true);
ruleListView.setOnItemLongClickListener(new OnItemLongClickListener()
@ -112,7 +109,6 @@ public class ActivityMainRules extends ActivityGeneric
private static class RuleArrayAdapter extends ArrayAdapter<Rule>
{
public RuleArrayAdapter(Context context, int resource, ArrayList<Rule> objects)
{
super(context, resource, objects);
@ -163,13 +159,13 @@ public class ActivityMainRules extends ActivityGeneric
if(AutomationService.isMyServiceRunning(this))
bindToService();
if(requestCode == 3000) //add Rule
if(requestCode == requestCodeCreateRule) //add Rule
{
ruleToEdit = null; //clear cache
updateListView();
}
if(requestCode == 4000) //editRule
if(requestCode == requestCodeChangeRule) //editRule
{
ruleToEdit = null; //clear cache
updateListView();
@ -190,7 +186,7 @@ public class ActivityMainRules extends ActivityGeneric
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.whatToDoWithRule));
alertDialogBuilder.setItems(new String[]{ getResources().getString(R.string.runManually), getResources().getString(R.string.edit), getResources().getString(R.string.deleteCapital) }, new DialogInterface.OnClickListener()
alertDialogBuilder.setItems(new String[]{ getResources().getString(R.string.runManually), getResources().getString(R.string.edit), getResources().getString(R.string.deleteCapital), getResources().getString(R.string.clone) }, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
@ -212,11 +208,22 @@ public class ActivityMainRules extends ActivityGeneric
case 1:
ruleToEdit = ruleThisIsAbout;
Intent manageSpecificRuleIntent = new Intent (ActivityMainRules.this, ActivityManageRule.class);
startActivityForResult(manageSpecificRuleIntent, 4000);
startActivityForResult(manageSpecificRuleIntent, requestCodeChangeRule);
break;
case 2:
if(ruleThisIsAbout.delete())
{
ruleToEdit = null; //clear cache
updateListView();
}
break;
case 3:
ruleToEdit = ruleThisIsAbout;
if(ruleToEdit.cloneRule(ActivityMainRules.this))
{
ruleToEdit = null; //clear cache
updateListView();
}
break;
}
}
@ -229,6 +236,11 @@ public class ActivityMainRules extends ActivityGeneric
public void updateListView()
{
Miscellaneous.logEvent("i", "ListView", "Attempting to update RuleListView", 4);
ruleList.clear();
for(Rule r : Rule.getRuleCollection())
ruleList.add(r);
try
{
if(ruleListView.getAdapter() == null)
@ -249,4 +261,4 @@ public class ActivityMainRules extends ActivityGeneric
// AlarmManager instance not prepared, yet.
}
}
}
}

View File

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.PendingIntent;
@ -42,8 +43,9 @@ public class ActivityMainScreen extends ActivityGeneric
private static ActivityMainScreen activityMainScreenInstance = null;
private ToggleButton toggleService, tbLockSound;
private Button bShowHelp, bPrivacy, bSettingsErase, bSettingsSetToDefault, bVolumeTest, bAddSoundLockTIme, bShareConfigAndLog;
private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNotePermissions, tvMainScreenNoteFeaturesFromOtherFlavor, tvMainScreenNoteLocationImpossibleBlameGoogle, tvMainScreenNoteNews, tvlockSoundDuration, tvFileStoreLocation;
private Button bShowHelp, bPrivacy, bSettingsErase, bAddSoundLockTIme;
private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNotePermissions, tvMainScreenNoteFeaturesFromOtherFlavor, tvMainScreenNoteLocationImpossibleBlameGoogle, tvMainScreenNoteNews, tvlockSoundDuration;
private static boolean updateNoteDisplayed = false;
private ListView lvRuleHistory;
private ArrayAdapter<Rule> ruleHistoryListViewAdapter;
@ -77,11 +79,7 @@ public class ActivityMainScreen extends ActivityGeneric
tvMainScreenNoteLocationImpossibleBlameGoogle = (TextView) findViewById(R.id.tvMainScreenNoteLocationImpossibleBlameGoogle);
tvMainScreenNoteNews = (TextView) findViewById(R.id.tvMainScreenNoteNews);
tvlockSoundDuration = (TextView)findViewById(R.id.tvlockSoundDuration);
tvFileStoreLocation = (TextView)findViewById(R.id.tvFileStoreLocation);
tbLockSound = (ToggleButton) findViewById(R.id.tbLockSound);
bVolumeTest = (Button) findViewById(R.id.bVolumeTest);
bSettingsSetToDefault = (Button) findViewById(R.id.bSettingsSetToDefault);
bShareConfigAndLog = (Button) findViewById(R.id.bShareConfigAndLog);
toggleService = (ToggleButton) findViewById(R.id.tbArmMastListener);
toggleService.setChecked(AutomationService.isMyServiceRunning(this));
toggleService.setOnCheckedChangeListener(new OnCheckedChangeListener()
@ -136,18 +134,8 @@ public class ActivityMainScreen extends ActivityGeneric
@Override
public void onClick(View v)
{
Intent myIntent = new Intent(ActivityMainScreen.this, ActivitySettings.class);
startActivityForResult(myIntent, 6000);
}
});
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);
}
});
@ -183,24 +171,6 @@ public class ActivityMainScreen extends ActivityGeneric
builder.create().show();
}
});
bSettingsSetToDefault.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
getDefaultSettingsDialog(ActivityMainScreen.this).show();
}
});
bShareConfigAndLog.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
getShareConfigAndLogDialogue(ActivityMainScreen.this).show();
}
});
lvRuleHistory.setOnTouchListener(new OnTouchListener()
{
@ -264,81 +234,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;
}
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);
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(ActivityMainScreen.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;
}
public static ActivityMainScreen getActivityMainScreenInstance()
{
return activityMainScreenInstance;
@ -430,9 +325,9 @@ public class ActivityMainScreen extends ActivityGeneric
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, Miscellaneous.getAnyContext())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, Miscellaneous.getAnyContext())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, Miscellaneous.getAnyContext())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, Miscellaneous.getAnyContext())
)
activityMainScreenInstance.tvActivePoi.setText(activityMainScreenInstance.getResources().getString(R.string.stillGettingPosition));
else
@ -513,36 +408,17 @@ public class ActivityMainScreen extends ActivityGeneric
else
activityMainScreenInstance.checkForNews();
if(BuildConfig.FLAVOR.equals("apkFlavor") && Settings.automaticUpdateCheck)
{
Calendar now = Calendar.getInstance();
if (Settings.lastUpdateCheck == Settings.default_lastUpdateCheck || now.getTimeInMillis() >= Settings.lastUpdateCheck + (long)(Settings.updateCheckFrequencyDays * 24 * 60 * 60 * 1000))
{
activityMainScreenInstance.checkForUpdate();
}
}
Settings.considerDone(Settings.constNewsOptInDone);
Settings.writeSettings(Miscellaneous.getAnyContext());
String folder = Miscellaneous.getWriteableFolder();
if(folder != null && folder.length() > 0)
{
activityMainScreenInstance.tvFileStoreLocation.setText(String.format(activityMainScreenInstance.getResources().getString(R.string.filesStoredAt), folder));
activityMainScreenInstance.tvFileStoreLocation.setOnClickListener(new 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(activityMainScreenInstance.getPackageManager(), 0) != null)
{
activityMainScreenInstance.startActivity(intent);
}
else
{
// if you reach this place, it means there is no any file
// explorer app installed on your device
Toast.makeText(activityMainScreenInstance, activityMainScreenInstance.getResources().getString(R.string.noFileManageInstalled), Toast.LENGTH_LONG).show();
}
}
});
}
}
}
@ -598,16 +474,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))
@ -732,6 +598,15 @@ public class ActivityMainScreen extends ActivityGeneric
Miscellaneous.messageBox(title, text, ActivityMainScreen.getActivityMainScreenInstance());
}
synchronized void checkForUpdate()
{
if(Settings.automaticUpdateCheck)
{
AsyncTasks.AsyncTaskUpdateCheck updateCheckTask = new AsyncTasks.AsyncTaskUpdateCheck();
updateCheckTask.execute(ActivityMainScreen.this);
}
}
synchronized void checkForNews()
{
if(Settings.displayNewsOnMainScreen)
@ -761,4 +636,38 @@ public class ActivityMainScreen extends ActivityGeneric
Miscellaneous.logEvent("e", "Error displaying news", Log.getStackTraceString(e), 3);
}
}
public void processUpdateCheckResult(Boolean result)
{
if(result && !updateNoteDisplayed)
{
updateNoteDisplayed = true;
AlertDialog.Builder updateNoteBuilder = new AlertDialog.Builder(ActivityMainScreen.this);
updateNoteBuilder.setMessage(getResources().getString(R.string.updateAvailable));
updateNoteBuilder.setPositiveButton(getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i)
{
String url = "https://server47.de/automation/";
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent);
updateNoteDisplayed = false;
}
});
updateNoteBuilder.setNegativeButton(getResources().getString(R.string.no), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i)
{
updateNoteDisplayed = false;
}
});
updateNoteBuilder.show();
}
AsyncTasks.AsyncTaskUpdateCheck.checkRunning = false;
}
}

View File

@ -13,12 +13,17 @@ import com.jens.automation2.receivers.NfcReceiver;
@SuppressLint("NewApi")
public class ActivityMainTabLayout extends TabActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tab_layout);
Settings.readFromPersistentStorage(ActivityMainTabLayout.this);
if(Settings.tabsPlacement == 1)
setContentView(R.layout.main_tab_layout_tabs_at_bottom);
else
setContentView(R.layout.main_tab_layout_tabs_at_top);
TabHost tabHost = getTabHost();

View File

@ -0,0 +1,370 @@
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 org.apache.commons.lang3.StringUtils;
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, tvAppVersion;
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);
tvAppVersion = (TextView)findViewById(R.id.tvAppVersion);
tvAppVersion.setText(
"Version: " + BuildConfig.VERSION_NAME + Miscellaneous.lineSeparator +
"Version code: " + String.valueOf(BuildConfig.VERSION_CODE) + Miscellaneous.lineSeparator +
"Flavor: " + BuildConfig.FLAVOR
);
}
@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())
{
/*
On some few users' devices it seems this caused a crash because file.getName() was null.
The reason for that remains unknown, but we don't want the export to crash because of it.
*/
if(!StringUtils.isEmpty(file.getName()))
{
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);
emailBody.append("Flavor: " + BuildConfig.FLAVOR);
Uri uri = Uri.parse("content://com.jens.automation2/" + Settings.zipFileName);
Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", emailBody.toString(), uri);
}
});
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

@ -8,7 +8,6 @@ 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;
@ -27,7 +26,7 @@ public class ActivityManageActionPlaySound extends Activity
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_play_sound);
setContentView(R.layout.activity_manage_action_play_sound);
chkPlaySoundAlwaysPlay = (CheckBox)findViewById(R.id.chkPlaySoundAlwaysPlay);
etSelectedSoundFile = (EditText)findViewById(R.id.etSelectedSoundFile);

View File

@ -25,6 +25,7 @@ public class ActivityManageActionSendTextMessage extends Activity
EditText etPhoneNumber, etSendTextMessage;
protected final static int requestCodeForContactsPermissions = 9876;
protected final static int requestCodeGetContact = 3235;
// private String existingUrl = "";
@ -35,7 +36,7 @@ public class ActivityManageActionSendTextMessage 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);
@ -83,20 +84,10 @@ public class ActivityManageActionSendTextMessage extends Activity
etPhoneNumber.setText(parameters[0]);
etSendTextMessage.setText(parameters[1]);
}
// String url = getIntent().getStringExtra("urlToTrigger");
// if(url != null)
// existingUrl = url;
}
private void backToRuleManager()
{
// Intent returnIntent = new Intent();
// returnIntent.putExtra("urlToTrigger", existingUrl);
// setResult(RESULT_OK, returnIntent);
if(edit && resultingAction != null)
{
ActivityManageActionSendTextMessage.resultingAction.setParameter2(etPhoneNumber.getText().toString() + Actions.smsSeparator + etSendTextMessage.getText().toString());
@ -145,16 +136,14 @@ public class ActivityManageActionSendTextMessage 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)
{

View File

@ -24,7 +24,7 @@ public class ActivityManageActionSpeakText 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);

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;
@ -37,12 +39,21 @@ import java.util.List;
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, etSelectedActivity;
Button bSelectApp, bAddIntentPair, bSaveActionStartOtherActivity;
EditText etParameterName, etParameterValue, etPackageName, etActivityOrActionPath;
Button bSelectApp, bAddIntentPair, bSaveActionStartOtherActivity, showStartProgramExamples;
Spinner spinnerParameterType;
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>
{
@ -71,7 +82,7 @@ public class ActivityManageActionStartActivity 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;
@ -267,7 +278,8 @@ public class ActivityManageActionStartActivity extends Activity
public void onClick(DialogInterface dialog, int which)
{
ActivityInfo ai = ActivityManageActionStartActivity.getActivityInfoForPackageNameAndActivityName(packageName, activityArray[which]);
etSelectedActivity.setText(ai.packageName + ";" + ai.name);
etPackageName.setText(ai.packageName);
etActivityOrActionPath.setText(ai.name);
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
@ -279,7 +291,7 @@ public class ActivityManageActionStartActivity 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);
@ -288,7 +300,14 @@ public class ActivityManageActionStartActivity extends Activity
bAddIntentPair = (Button)findViewById(R.id.bAddIntentPair);
bSaveActionStartOtherActivity = (Button)findViewById(R.id.bSaveActionStartOtherActivity);
spinnerParameterType = (Spinner)findViewById(R.id.spinnerParameterType);
etSelectedActivity = (EditText) findViewById(R.id.etSelectedApplication);
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, ActivityManageActionStartActivity.supportedIntentTypes);
spinnerParameterType.setAdapter(intentTypeSpinnerAdapter);
@ -324,14 +343,34 @@ public class ActivityManageActionStartActivity extends Activity
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(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);
@ -339,6 +378,19 @@ public class ActivityManageActionStartActivity 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);
}
});
@ -393,6 +445,26 @@ public class ActivityManageActionStartActivity 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)
@ -404,22 +476,46 @@ public class ActivityManageActionStartActivity 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)
{
etSelectedActivity.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();
}
}
@ -433,39 +529,51 @@ public class ActivityManageActionStartActivity extends Activity
private boolean saveAction()
{
if(etSelectedActivity.getText().toString().length() == 0)
if(rbStartAppSelectByActivity.isChecked())
{
Toast.makeText(ActivityManageActionStartActivity.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;
}
}
// else
// {
// Intent testIntent = new Intent(ActivityManageActionStartActivity.this, etSelectedActivity);
// Intent externalActivityIntent = new Intent(Intent.ACTION_MAIN);
// externalActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// externalActivityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
// externalActivityIntent.setClassName(packageName, className);
//
// boolean activityExists = externalActivityIntent.resolveActivityInfo(getPackageManager(), 0) != null;
// }
if(etSelectedActivity.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;
String parameter2 = "";
if(etSelectedActivity.getText().toString().contains(";"))
parameter2 = etSelectedActivity.getText().toString();
if(rbStartAppSelectByActivity.isChecked())
parameter2 += etPackageName.getText().toString() + ";" + etActivityOrActionPath.getText().toString();
else
parameter2 = "dummyPkg;" + etSelectedActivity.getText().toString();
{
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;

View File

@ -39,7 +39,7 @@ public class ActivityManageActionTriggerUrl 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);

View File

@ -0,0 +1,85 @@
package com.jens.automation2;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
public class ActivityManageActionVibrate extends Activity
{
TextView etVibratePattern;
Button bTestVibratePattern, bSaveVibratePattern;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_action_vibrate);
etVibratePattern = (EditText)findViewById(R.id.etVibratePattern);
bTestVibratePattern = (Button)findViewById(R.id.bTestVibratePattern);
bSaveVibratePattern = (Button)findViewById(R.id.bSaveVibratePattern);
bSaveVibratePattern.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
if(checkInput())
{
Intent answer = new Intent();
answer.putExtra("vibratePattern", etVibratePattern.getText().toString());
setResult(RESULT_OK, answer);
finish();
}
}
});
bTestVibratePattern.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if(checkInput())
{
if (ActivityPermissions.havePermission(Manifest.permission.VIBRATE, ActivityManageActionVibrate.this))
{
String pattern = etVibratePattern.getText().toString();
Actions.vibrate(false, pattern);
}
}
}
});
Intent input = getIntent();
if(input.hasExtra("vibratePattern"))
etVibratePattern.setText(input.getStringExtra("vibratePattern"));
}
boolean checkInput()
{
String vibratePattern = etVibratePattern.getText().toString();
String regex = "^[0-9,]+$";
if(StringUtils.isEmpty(vibratePattern) || !vibratePattern.matches(regex) || vibratePattern.substring(0, 1).equals(",") || vibratePattern.substring(vibratePattern.length()-1).equals(","))
{
Toast.makeText(ActivityManageActionVibrate.this, getResources().getString(R.string.pleaseEnterValidVibrationPattern), Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}

View File

@ -13,6 +13,7 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
@ -21,6 +22,10 @@ import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Toast;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class ActivityManagePoi extends Activity
{
public LocationManager myLocationManager;
@ -31,6 +36,11 @@ public class ActivityManagePoi extends Activity
Button bGetPosition, bSavePoi;
ImageButton ibShowOnMap;
EditText guiPoiName, guiPoiLatitude, guiPoiLongitude, guiPoiRadius;
Calendar locationSearchStart = null;
Timer timer = null;
final static int defaultRadius = 250;
final static int searchTimeout = 120;
private static ProgressDialog progressDialog;
@ -47,7 +57,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);
@ -104,7 +114,7 @@ public class ActivityManagePoi extends Activity
myLocationManager.removeUpdates(myLocationListenerGps);
ActivityMainPoi.poiToEdit = new PointOfInterest();
ActivityMainPoi.poiToEdit.setLocation(new Location("POINT_LOCATION"));
if(loadFormValuesToVariable())
if(loadFormValuesToVariable(false))
if(ActivityMainPoi.poiToEdit.create(this))
{
this.setResult(RESULT_OK);
@ -114,7 +124,7 @@ public class ActivityManagePoi extends Activity
private void changePoi()
{
myLocationManager.removeUpdates(myLocationListenerGps);
if(loadFormValuesToVariable())
if(loadFormValuesToVariable(false))
if(ActivityMainPoi.poiToEdit.change(this))
{
this.setResult(RESULT_OK);
@ -150,47 +160,142 @@ public class ActivityManagePoi extends Activity
}
else
{
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGettingPositionWithProvider) + " " + provider1, 3);
myLocationManager.requestLocationUpdates(provider1, 500, Settings.satisfactoryAccuracyNetwork, myLocationListenerNetwork);
locationSearchStart = Calendar.getInstance();
startTimeout();
if(!Settings.privacyLocationing)
{
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGettingPositionWithProvider) + " " + provider1, 3);
myLocationManager.requestLocationUpdates(provider1, 500, Settings.satisfactoryAccuracyNetwork, myLocationListenerNetwork);
}
else
Miscellaneous.logEvent("i", "POI Manager", "Skipping network location query because private locationing is active.", 4);
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGettingPositionWithProvider) + " " + provider2, 3);
myLocationManager.requestLocationUpdates(provider2, 500, Settings.satisfactoryAccuracyGps, myLocationListenerGps);
}
}
private void compareLocations()
private void startTimeout()
{
if(timer != null)
stopTimeout();
timer = new Timer();
class TimeoutTask extends TimerTask
{
public void run()
{
evaluateLocationResults();
}
}
Miscellaneous.logEvent("i", "POI Manager", "Starting timeout for location search: " + String.valueOf(searchTimeout) + " seconds", 5);
TimerTask timeoutTask = new TimeoutTask();
timer.schedule(timeoutTask, searchTimeout * 1000);
}
private void stopTimeout()
{
Miscellaneous.logEvent("i", "POI Manager", "Stopping timeout for location search.", 5);
if(timer != null)
{
timer.purge();
timer.cancel();
}
}
private void evaluateLocationResults()
{
/*
Procedure:
If we get a GPS result we take it and suggest a default minimum radius.
If private locationing is active that's the only possible outcome other than a timeout.
If private locationing is not active
If we get a network
*/
// We have GPS
if(locationGps != null)
{
myLocationManager.removeUpdates(myLocationListenerNetwork);
guiPoiLatitude.setText(String.valueOf(locationGps.getLatitude()));
guiPoiLongitude.setText(String.valueOf(locationGps.getLongitude()));
String text;
if(locationNetwork != null)
{
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.comparing), 4);
double variance = locationGps.distanceTo(locationNetwork);
String text = getResources().getString(R.string.distanceBetween) + " " + String.valueOf(Math.round(variance)) + " " + getResources().getString(R.string.radiusSuggestion);
// 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)
// {
// String text = "Positioning via network is off by " + variance + " meters. The radius you specify shouldn't be smaller than that.";
getDialog(text, Math.round(variance) + 1).show();
// Toast.makeText(getBaseContext(), "Positioning via network is off by " + variance + " meters. The radius you specify shouldn't be smaller than that.", Toast.LENGTH_LONG).show();
// }
text = String.format(getResources().getString(R.string.distanceBetween), Math.round(variance));
getRadiusConfirmationDialog(text, Math.round(variance) + 1).show();
}
else
{
progressDialog.dismiss();
myLocationManager.removeUpdates(myLocationListenerNetwork);
guiPoiRadius.setText("250");
text = String.format(getResources().getString(R.string.locationFound), defaultRadius);
getRadiusConfirmationDialog(text, defaultRadius).show();
}
Miscellaneous.logEvent("i", "POI Manager", text, 4);
} // we have a great network signal:
else if(locationNetwork != null && locationNetwork.getAccuracy() <= Settings.satisfactoryAccuracyGps && locationNetwork.getAccuracy() <= defaultRadius)
{
/*
We do not yet have a GPS result. But we have a network result that is good enough
to accept it a sole result. In that case we suggest a default radius, no variance.
*/
guiPoiLatitude.setText(String.valueOf(locationNetwork.getLatitude()));
guiPoiLongitude.setText(String.valueOf(locationNetwork.getLongitude()));
String text = String.format(getResources().getString(R.string.locationFound), defaultRadius);
Miscellaneous.logEvent("i", "POI Manager", text, 4);
getRadiusConfirmationDialog(text, defaultRadius).show();
}
else if( // we have a bad network signal and nothing else, GPS result may still come in
locationNetwork != null
&&
Calendar.getInstance().getTimeInMillis()
<
(locationSearchStart.getTimeInMillis() + ((long)searchTimeout * 1000))
)
{
// Only a network location was found and it is also not very accurate.
}
else if( // we have a bad network signal and nothing else, timeout has expired, nothing else can possibly come in
locationNetwork != null
&&
Calendar.getInstance().getTimeInMillis()
>
(locationSearchStart.getTimeInMillis() + ((long)searchTimeout * 1000))
)
{
// Only a network location was found and it is also not very accurate.
guiPoiLatitude.setText(String.valueOf(locationNetwork.getLatitude()));
guiPoiLongitude.setText(String.valueOf(locationNetwork.getLongitude()));
String text = String.format(getResources().getString(R.string.locationFoundInaccurate), defaultRadius);
getRadiusConfirmationDialog(text, defaultRadius).show();
Miscellaneous.logEvent("i", "POI Manager", text, 4);
}
else
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logNotAllMeasurings), 4);
{
String text = String.format(getResources().getString(R.string.noLocationCouldBeFound), String.valueOf(searchTimeout));
Miscellaneous.logEvent("i", "POI Manager", text, 2);
if(myLocationListenerNetwork != null)
myLocationManager.removeUpdates(myLocationListenerNetwork);
myLocationManager.removeUpdates(myLocationListenerGps);
progressDialog.dismiss();
getErrorDialog(text).show();
}
}
private AlertDialog getNotificationDialog(String text)
@ -202,15 +307,6 @@ public class ActivityManagePoi extends Activity
@Override
public void onClick(DialogInterface dialog, int which)
{
// switch(which)
// {
// case DialogInterface.BUTTON_POSITIVE:
// guiPoiRadius.setText(String.valueOf(value));
// break;
// case DialogInterface.BUTTON_NEGATIVE:
// break;
// }
progressDialog = ProgressDialog.show(ActivityManagePoi.this, "", getResources().getString(R.string.gettingPosition), true, true);
getLocation();
}
@ -221,8 +317,11 @@ public class ActivityManagePoi extends Activity
return alertDialog;
}
private AlertDialog getDialog(String text, final double value)
private AlertDialog getRadiusConfirmationDialog(String text, final double value)
{
stopTimeout();
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{
@ -247,10 +346,31 @@ public class ActivityManagePoi extends Activity
return alertDialog;
}
private AlertDialog getErrorDialog(String text)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
progressDialog.dismiss();
}
};
alertDialogBuilder.setMessage(text);
alertDialogBuilder.setPositiveButton(getResources().getString(R.string.ok), null);
if (Looper.myLooper() == null)
Looper.prepare();
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
public class MyLocationListenerGps implements LocationListener
{
@Override
public void onLocationChanged(Location location)
{
@ -260,10 +380,11 @@ public class ActivityManagePoi extends Activity
// {
// Miscellaneous.logEvent("i", "POI Manager", "satisfactoryNetworkAccuracy of " + String.valueOf(Settings.SATISFACTORY_ACCURACY_GPS) + "m reached. Removing location updates...");
Miscellaneous.logEvent("i", "POI Manager", "Unsubscribing from GPS location updates.", 5);
myLocationManager.removeUpdates(this);
locationGps = location;
compareLocations();
evaluateLocationResults();
// }
}
@ -287,66 +408,27 @@ public class ActivityManagePoi extends Activity
// TODO Auto-generated method stub
}
}
// public class MyLocationListenerWifi implements LocationListener
// {
//
// @Override
// public void onLocationChanged(Location location)
// {
// Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.gotGpsUpdate) + " " + String.valueOf(location.getAccuracy()));
// // Deactivate when accuracy reached
//// if(location.getAccuracy() < Settings.SATISFACTORY_ACCURACY_GPS)
//// {
//// Miscellaneous.logEvent("i", "POI Manager", "satisfactoryNetworkAccuracy of " + String.valueOf(Settings.SATISFACTORY_ACCURACY_GPS) + "m reached. Removing location updates...");
//
// myLocationManager.removeUpdates(this);
// locationGps = location;
//
// compareLocations();
//// }
// }
//
// @Override
// public void onProviderDisabled(String provider)
// {
// // TODO Auto-generated method stub
//
// }
//
// @Override
// public void onProviderEnabled(String provider)
// {
// // TODO Auto-generated method stub
//
// }
//
// @Override
// public void onStatusChanged(String provider, int status, Bundle extras)
// {
// // TODO Auto-generated method stub
//
// }
//
// }
public class MyLocationListenerNetwork implements LocationListener
{
@Override
public void onLocationChanged(Location location)
{
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGotNetworkUpdate) + " " + String.valueOf(location.getAccuracy()), 3);
myLocationManager.removeUpdates(this);
locationNetwork = location;
// Deactivate when accuracy reached
// if(location.getAccuracy() < Settings.SATISFACTORY_ACCURACY_GPS)
// {
// String text = "Network position found. satisfactoryNetworkAccuracy of " + String.valueOf(Settings.SATISFACTORY_ACCURACY_NETWORK) + "m reached. Removing location updates...";
// Miscellaneous.logEvent("i", "POI Manager", text);
myLocationManager.removeUpdates(this);
locationNetwork = location;
compareLocations();
// }
if(location.getAccuracy() <= Settings.satisfactoryAccuracyGps)
{
// Accuracy is so good that we don't need to wait for GPS result
Miscellaneous.logEvent("i", "POI Manager", "Unsubscribing from network location updates.", 5);
myLocationManager.removeUpdates(myLocationListenerGps);
}
evaluateLocationResults();
}
@Override
@ -369,7 +451,6 @@ public class ActivityManagePoi extends Activity
// TODO Auto-generated method stub
}
}
public void editPoi(PointOfInterest poi)
@ -380,19 +461,22 @@ public class ActivityManagePoi extends Activity
guiPoiRadius.setText(String.valueOf(poi.getRadius()));
}
public boolean loadFormValuesToVariable()
public boolean loadFormValuesToVariable(boolean checkOnlyCoordinates)
{
if(ActivityMainPoi.poiToEdit == null)
ActivityMainPoi.poiToEdit = new PointOfInterest();
if(guiPoiName.getText().length() == 0)
if(!checkOnlyCoordinates)
{
Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false;
if (guiPoiName.getText().length() == 0)
{
Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false;
}
else
ActivityMainPoi.poiToEdit.setName(guiPoiName.getText().toString());
}
else
ActivityMainPoi.poiToEdit.setName(guiPoiName.getText().toString());
if(ActivityMainPoi.poiToEdit.getLocation() == null)
ActivityMainPoi.poiToEdit.setLocation(new Location("POINT_LOCATION"));
@ -415,28 +499,31 @@ public class ActivityManagePoi extends Activity
Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidLongitude), Toast.LENGTH_LONG).show();
return false;
}
try
if(!checkOnlyCoordinates)
{
ActivityMainPoi.poiToEdit.setRadius(Double.parseDouble(guiPoiRadius.getText().toString()), this);
try
{
ActivityMainPoi.poiToEdit.setRadius(Double.parseDouble(guiPoiRadius.getText().toString()), this);
}
catch (NumberFormatException e)
{
Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidRadius), Toast.LENGTH_LONG).show();
return false;
}
catch (Exception e)
{
Toast.makeText(this, getResources().getString(R.string.unknownError), Toast.LENGTH_LONG).show();
return false;
}
}
catch(NumberFormatException e)
{
Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidRadius), Toast.LENGTH_LONG).show();
return false;
}
catch (Exception e)
{
Toast.makeText(this, getResources().getString(R.string.unknownError), Toast.LENGTH_LONG).show();
return false;
}
return true;
}
private void showOnMap()
{
if(loadFormValuesToVariable())
if(loadFormValuesToVariable(true))
{
try
{

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

@ -99,6 +99,13 @@ public class ActivityManageRule extends Activity
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;
final static int requestCodeActionSendTextMessageAdd = 5001;
final static int requestCodeActionVibrateAdd = 801;
final static int requestCodeActionVibrateEdit = 802;
public static ActivityManageRule getInstance()
{
@ -224,27 +231,11 @@ public class ActivityManageRule extends Activity
Trigger selectedTrigger = (Trigger)triggerListView.getItemAtPosition(arg2);
switch(selectedTrigger.getTriggerType())
{
// case batteryLevel:
// break;
// case charging:
// break;
// case noiseLevel:
// break;
// case pointOfInterest:
// break;
// case process_started_stopped:
// break;
// case speed:
// break;
case timeFrame:
ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = selectedTrigger;
Intent timeFrameEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerTimeFrame.class);
startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeEdit);
break;
// case usb_host_connection:
// break;
// case wifiConnection:
// break;
case bluetoothConnection:
ActivityManageTriggerBluetooth.editedBluetoothTrigger = selectedTrigger;
Intent bluetoothEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerBluetooth.class);
@ -256,6 +247,19 @@ public class ActivityManageRule extends Activity
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;
}
@ -289,14 +293,6 @@ public class ActivityManageRule extends Activity
Action a = (Action)actionListView.getItemAtPosition(arg2);
switch(a.getAction())
{
// case changeSoundProfile:
// break;
// case disableScreenRotation:
// break;
// case enableScreenRotation:
// break;
// case setAirplaneMode:
// break;
case startOtherActivity:
Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionStartActivity.class);
ActivityManageActionStartActivity.resultingAction = a;
@ -305,31 +301,10 @@ public class ActivityManageRule extends Activity
break;
case triggerUrl:
Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityManageActionTriggerUrl.class);
// activityEditTriggerUrlIntent.putExtra("urlToTrigger", a.getParameter2());
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, ActivityManageActionSpeakText.class);
ActivityManageActionSpeakText.resultingAction = a;
@ -344,11 +319,15 @@ public class ActivityManageRule extends Activity
break;
case setScreenBrightness:
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 vibrate:
Intent activityEditVibrateIntent = new Intent(ActivityManageRule.this, ActivityManageActionVibrate.class);
activityEditVibrateIntent.putExtra("vibratePattern", a.getParameter2());
startActivityForResult(activityEditVibrateIntent, requestCodeActionVibrateEdit);
break;
case playSound:
Intent actionPlaySoundIntent = new Intent(context, ActivityManageActionPlaySound.class);
actionPlaySoundIntent.putExtra("edit", true);
@ -552,7 +531,15 @@ 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)
@ -567,7 +554,13 @@ public class ActivityManageRule extends Activity
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
@ -676,13 +669,6 @@ public class ActivityManageRule extends Activity
String[] choices = (String[]) choicesList.toArray(new String[choicesList.size()]);
getTriggerNoiseDialog(myContext, choices).show();
}
// else if(triggerType.equals(Trigger.Event_Enum.timeFrame))
// {
// newTrigger.setTriggerType(Trigger.Event_Enum.timeFrame);
// ActivityManageTimeFrame.editedTimeFrameTrigger = null;
// Intent timeFrameEditor = new Intent(myContext, ActivityManageTimeFrame.class);
// startActivityForResult(timeFrameEditor, 2000);
// }
else if(triggerType.equals(Trigger_Enum.wifiConnection))
{
newTrigger.setTriggerType(Trigger_Enum.wifiConnection);
@ -693,7 +679,6 @@ public class ActivityManageRule extends Activity
progressDialog = ProgressDialog.show(myContext, null, getResources().getString(R.string.gettingListOfInstalledApplications), true, false);
newTrigger.setTriggerType(Trigger_Enum.process_started_stopped);
new GenerateApplicationSelectionsDialogTask().execute(ActivityManageRule.this);
// getTriggerRunningProcessDialog1(myContext).show();
}
else if(triggerType.equals(Trigger_Enum.phoneCall))
{
@ -826,7 +811,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();
}
@ -1133,6 +1118,25 @@ public class ActivityManageRule extends Activity
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
@ -1170,7 +1174,6 @@ public class ActivityManageRule extends Activity
//add notification
if(resultCode == RESULT_OK)
{
//newTrigger.setNfcTagId(ActivityManageNfc.generatedId);
ruleToEdit.getTriggerSet().add(newTrigger);
newTrigger.setTriggerParameter2(
@ -1182,8 +1185,6 @@ public class ActivityManageRule extends Activity
);
this.refreshTriggerList();
}
else
Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5);
}
else if(requestCode == requestCodeTriggerNfcNotificationEdit)
{
@ -1193,6 +1194,23 @@ public class ActivityManageRule extends Activity
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)
@ -1255,6 +1273,25 @@ public class ActivityManageRule extends Activity
this.refreshActionList();
}
}
else if(requestCode == requestCodeActionVibrateAdd)
{
if(resultCode == RESULT_OK)
{
newAction.setParameter2(data.getStringExtra("vibratePattern"));
ruleToEdit.getActionSet().add(newAction);
this.refreshActionList();
}
}
else if(requestCode == requestCodeActionVibrateEdit)
{
if(resultCode == RESULT_OK)
{
if(data.hasExtra("vibratePattern"))
ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra("vibratePattern"));
this.refreshActionList();
}
}
else if(requestCode == requestCodeActionPlaySoundAdd)
{
if(resultCode == RESULT_OK)
@ -1332,6 +1369,8 @@ public class ActivityManageRule extends Activity
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.vibrate.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.vibrate));
else if(types[i].toString().equals(Action_Enum.sendTextMessage.toString()))
{
// if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this))
@ -1396,7 +1435,7 @@ public class ActivityManageRule extends Activity
{
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();
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()))
@ -1437,13 +1476,13 @@ public class ActivityManageRule extends Activity
}
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();
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();
}
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDataConnection.toString()))
{
@ -1461,14 +1500,13 @@ public class ActivityManageRule extends Activity
}
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);
ActivityManageActionSendTextMessage.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityManageActionSendTextMessage.class);
startActivityForResult(editTriggerIntent, 5001);
startActivityForResult(editTriggerIntent, requestCodeActionSendTextMessageAdd);
}
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playMusic.toString()))
@ -1477,6 +1515,12 @@ public class ActivityManageRule extends Activity
ruleToEdit.getActionSet().add(newAction);
refreshActionList();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.vibrate.toString()))
{
newAction.setAction(Action_Enum.vibrate);
Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionVibrate.class);
startActivityForResult(intent, requestCodeActionVibrateAdd);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setScreenBrightness.toString()))
{
newAction.setAction(Action_Enum.setScreenBrightness);
@ -1594,63 +1638,6 @@ public class ActivityManageRule extends Activity
return alertDialog;
}
/*private AlertDialog getActionStartActivityDialog1(final Context myContext)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectApplication));
final String[] applicationArray = ActivityManageStartActivity.getApplicationNameListString(ActivityManageSpecificRule.this);
alertDialogBuilder.setItems(applicationArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
getActionStartActivityDialog2(myContext, applicationArray[which]).show();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
private AlertDialog getActionStartActivityDialog2(final Context myContext, String applicationName)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectPackageOfApplication));
final String[] packageArray = ActivityManageStartActivity.getPackageListString(ActivityManageSpecificRule.this, applicationName);
alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
getActionStartActivityDialog3(ActivityManageSpecificRule.this, packageArray[which]).show();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
private AlertDialog getActionStartActivityDialog3(final Context myContext, final String packageName)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectActivityToBeStarted));
final String activityArray[] = ActivityManageStartActivity.getActivityListForPackageName(packageName);
alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
ActivityInfo ai = ActivityManageStartActivity.getActivityInfoForPackageNameAndActivityName(packageName, activityArray[which]);
// Log.i("Selected", ai.packageName + " / " + ai.name);
newAction.setParameter2(ai.packageName + ";" + ai.name);
newAction.toString();
ruleToEdit.getActionSet().add(newAction);
refreshActionList();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}*/
private AlertDialog getActionWaitBeforeNextActionDialog(final Context myContext)
{
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

View File

@ -29,7 +29,7 @@ public class ActivityManageTriggerBluetooth 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);

View File

@ -32,13 +32,14 @@ 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;
boolean edit = false;
ProgressDialog progressDialog = null;
private static List<PackageInfo> pInfos = null;
public static Trigger resultingTrigger;
@ -250,7 +251,7 @@ public class ActivityManageTriggerNotification extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_trigger_notification);
setContentView(R.layout.activity_manage_trigger_notification);
etNotificationTitle = (EditText)findViewById(R.id.etNotificationTitle);
etNotificationText = (EditText)findViewById(R.id.etNotificationText);
@ -258,7 +259,7 @@ public class ActivityManageTriggerNotification extends Activity
bSaveTriggerNotification = (Button)findViewById(R.id.bSaveTriggerNotification);
spinnerTitleDirection = (Spinner)findViewById(R.id.spinnerTitleDirection);
spinnerTextDirection = (Spinner)findViewById(R.id.spinnerTextDirection);
tvSelectedApplication = (TextView)findViewById(R.id.etSelectedApplication);
tvSelectedApplication = (TextView)findViewById(R.id.etActivityOrActionPath);
chkNotificationDirection = (CheckBox)findViewById(R.id.chkNotificationDirection);
directions = new String[] {
@ -393,7 +394,5 @@ public class ActivityManageTriggerNotification extends Activity
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

@ -27,7 +27,7 @@ public class ActivityManageTriggerTimeFrame extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_trigger_timeframe);
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,6 +1,7 @@
package com.jens.automation2;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.PreferenceActivity;
@ -9,11 +10,18 @@ import com.jens.automation2.R.layout;
public class ActivitySettings extends PreferenceActivity
{
ListPreference lpStartScreenOptionsValues;
CheckBoxPreference chkPrefUpdateCheck;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(layout.settings);
addPreferencesFromResource(layout.activity_settings);
if(BuildConfig.FLAVOR.equals("apkFlavor"))
{
chkPrefUpdateCheck = (CheckBoxPreference) findPreference("automaticUpdateCheck");
chkPrefUpdateCheck.setEnabled(true);
}
}
}

View File

@ -0,0 +1,65 @@
package com.jens.automation2;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.util.ArrayList;
import java.util.Calendar;
public class AsyncTasks
{
public static class AsyncTaskUpdateCheck extends AsyncTask<Context, Void, Boolean>
{
public static boolean checkRunning = false;
@Override
protected Boolean doInBackground(Context... contexts)
{
if(checkRunning)
return false;
else
checkRunning = true;
try
{
String result = Miscellaneous.downloadURL("https://server47.de/automation/?action=getLatestVersionCode", null, null).trim();
int latestVersion = Integer.parseInt(result);
// At this point the update check itself has already been successful.
Settings.lastUpdateCheck = Calendar.getInstance().getTimeInMillis();
Settings.writeSettings(contexts[0]);
if (latestVersion > BuildConfig.VERSION_CODE)
{
// There's a new update
return true;
}
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "Error checking for update", Log.getStackTraceString(e), 3);
}
return false;
}
@Override
protected void onPostExecute(Boolean result)
{
try
{
ActivityMainScreen.getActivityMainScreenInstance().processUpdateCheckResult(result);
}
catch (NullPointerException e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the update check result, probably ActivityMainScreen isn't currently shown: " + Log.getStackTraceString(e), 2);
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the update check result: " + Log.getStackTraceString(e), 2);
}
}
}
}

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;
@ -121,14 +122,14 @@ public class AutomationService extends Service implements OnInitListener
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;
}
}
@ -196,7 +197,7 @@ public class AutomationService extends Service implements OnInitListener
if (checkStartupRequirements(this, startAtBoot))
{
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.logServiceStarting), 1);
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.logServiceStarting) + " VERSION_CODE: " + BuildConfig.VERSION_CODE + ", VERSION_NAME: " + BuildConfig.VERSION_NAME + ", flavor: " + BuildConfig.FLAVOR, 1);
startUpRoutine();
@ -210,7 +211,7 @@ public class AutomationService extends Service implements OnInitListener
ActivityMainScreen.updateMainScreen();
this.isRunning = true;
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.serviceStarted) + " " + String.format(this.getResources().getString(R.string.version), BuildConfig.VERSION_NAME + "(Build " + BuildConfig.VERSION_CODE + ")"), 1);
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.serviceStarted) + " VERSION_CODE: " + BuildConfig.VERSION_CODE + ", VERSION_NAME: " + BuildConfig.VERSION_NAME + ", flavor: " + BuildConfig.FLAVOR, 1);
Toast.makeText(this, this.getResources().getString(R.string.serviceStarted), Toast.LENGTH_LONG).show();
// ********** Test area **********
// Miscellaneous.logEvent("i", "setNetworkType", "bin hier.", 3);
@ -257,7 +258,8 @@ public class AutomationService extends Service implements OnInitListener
case reloadSettings:
Settings.readFromPersistentStorage(this);
applySettingsAndRules();
myLocationProvider.applySettingsAndRules();
if(myLocationProvider != null)
myLocationProvider.applySettingsAndRules();
break;
case updateNotification:
this.updateNotification();
@ -341,6 +343,9 @@ public class AutomationService extends Service implements OnInitListener
{
boolean displayNotification = false;
String rule = "";
outerLoop:
for(Rule r : Rule.getRuleCollection())
{
if(r.isRuleActive())
@ -353,7 +358,11 @@ public class AutomationService extends Service implements OnInitListener
// r.setRuleActive(false);
// r.change(AutomationService.this);
if(!displayNotification)
{
displayNotification = true;
rule = r.getName();
break outerLoop;
}
}
}
}
@ -361,18 +370,16 @@ public class AutomationService extends Service implements OnInitListener
if(displayNotification)
{
// Toast.makeText(Miscellaneous.getAnyContext(), "Require more permissions.", Toast.LENGTH_LONG).show();
// Update notification or show new one that notifiies of the lack or permissions.
Intent intent = new Intent(AutomationService.this, ActivityPermissions.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
Miscellaneous.logEvent("w", "Features disabled", "Features disabled because of rule " + rule, 5);
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();
}
}
@ -389,6 +396,9 @@ 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.logEvent("w", "Features disabled", "Background location disabled because Google to blame.", 5);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
Miscellaneous.createDismissableNotificationWithDelay(3300, getResources().getString(R.string.featuresDisabled), notificationIdRestrictions, pi);
else
@ -404,6 +414,8 @@ 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.logEvent("w", "Features disabled", "Background location disabled because Google to blame.", 5);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
Miscellaneous.createDismissableNotificationWithDelay(2200, getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
else
@ -445,7 +457,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();
@ -581,11 +594,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)
@ -593,9 +606,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

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Notification;
@ -21,11 +22,14 @@ 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 com.jens.automation2.location.LocationProvider;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import org.apache.http.HttpEntity;
@ -90,6 +94,7 @@ 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;
@ -116,15 +121,6 @@ public class Miscellaneous extends Service
if(url.toLowerCase().contains("https"))
{
connection = (HttpsURLConnection) urlObject.openConnection();
// if(Settings.httpAcceptAllCertificates)
// {
// SSLContext sc = SSLContext.getInstance("TLS");
// sc.init(null, getInsecureTrustManager(), new java.security.SecureRandom());
// HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Miscellaneous.disableSSLCertificateChecking();
// HttpsURLConnection.setDefaultHostnameVerifier(getInsecureHostnameVerifier());
// }
}
else
connection = (HttpURLConnection) urlObject.openConnection();
@ -332,7 +328,7 @@ public class Miscellaneous extends Service
migration:
if (!newConfigFile.exists())
{
if (ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, Miscellaneous.getAnyContext()))
if (ActivityPermissions.havePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Miscellaneous.getAnyContext()))
{
// We have the storage permission, probably because it's an old installation. Files should be migrated to app-specific folder.
@ -595,6 +591,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);
@ -1214,6 +1230,122 @@ public class Miscellaneous extends Service
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)
@ -1333,4 +1465,31 @@ public class Miscellaneous extends Service
{
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

@ -76,7 +76,7 @@ public class News
if(oldFilePath.exists())
oldFilePath.delete();
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.newsDisplayForXDays * 24 * 60 * 60 * 1000))
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == Settings.default_lastNewsPolltime || 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);

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;
}
@ -793,6 +805,7 @@ public class PointOfInterest implements Comparable<PointOfInterest>
{
String text = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.overlapBetweenPois), otherPoi.getName(), String.valueOf(overlap));
Miscellaneous.logEvent("w", "POI", text, 2);
// Miscellaneous.messageBox("POI", text, Miscellaneous.getAnyContext()).show();
Toast.makeText(Miscellaneous.getAnyContext(), text, Toast.LENGTH_LONG).show();
return false;
}

View File

@ -271,41 +271,45 @@ public class ReceiverCoordinator
ProcessListener.stopProcessListener(AutomationService.getInstance());
}
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor"))
{
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);;
if(runResult instanceof Boolean)
if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
{
boolean isRunning = (Boolean) runResult;
if (isRunning)
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if (runResult instanceof Boolean)
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "restartActivityDetectionReceiver", null);
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "restartActivityDetectionReceiver", null);
// ActivityDetectionReceiver.restartActivityDetectionReceiver();
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
}
}
else
{
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if(runResult instanceof Boolean)
else
{
boolean isRunning = (Boolean) runResult;
if (isRunning)
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if (runResult instanceof Boolean)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective(activityDetectionClassPath, "stopActivityDetectionReceiver", null);
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective(activityDetectionClassPath, "stopActivityDetectionReceiver", null);
// ActivityDetectionReceiver.stopActivityDetectionReceiver();
}
}
}
}

View File

@ -15,6 +15,7 @@ public class Settings implements SharedPreferences
public final static int lockSoundChangesInterval = 15;
public static final int newsPollEveryXDays = 3;
public static final int newsDisplayForXDays = 3;
public static final int updateCheckFrequencyDays = 7;
public static final String folderName = "Automation";
public static final String zipFileName = "automation.zip";
@ -59,12 +60,16 @@ public class Settings implements SharedPreferences
public static int activityDetectionRequiredProbability;
public static boolean privacyLocationing;
public static int startScreen;
public static int tabsPlacement;
public static boolean executeRulesAndProfilesWithSingleClick;
public static boolean displayNewsOnMainScreen;
public static boolean automaticUpdateCheck;
public static boolean lockSoundChanges;
public static boolean noticeAndroid9MicrophoneShown;
public static boolean noticeAndroid10WifiShown;
public static long lastNewsPolltime;
public static long lastUpdateCheck;
public static ArrayList<String> whatHasBeenDone;
@ -112,10 +117,13 @@ public class Settings implements SharedPreferences
protected static final int default_activityDetectionRequiredProbability = 75;
protected static final boolean default_privacyLocationing = false;
protected static final int default_startScreen = 0;
protected static final int default_tabsPlacement = 0;
protected static final boolean default_executeRulesAndProfilesWithSingleClick = false;
protected static final boolean default_displayNewsOnMainScreen = false;
protected static final boolean default_automaticUpdateCheck = false;
protected static final boolean default_lockSoundChanges = false;
protected static final long default_lastNewsPolltime = -1;
protected static final long default_lastUpdateCheck = -1;
@Override
public boolean contains(String arg0)
@ -247,8 +255,10 @@ public class Settings implements SharedPreferences
privacyLocationing = prefs.getBoolean("privacyLocationing", default_privacyLocationing);
startScreen = Integer.parseInt(prefs.getString("startScreen", String.valueOf(default_startScreen)));
tabsPlacement = Integer.parseInt(prefs.getString("tabsPlacement", String.valueOf(default_tabsPlacement)));
executeRulesAndProfilesWithSingleClick = prefs.getBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick);
automaticUpdateCheck = prefs.getBoolean("automaticUpdateCheck", default_automaticUpdateCheck);
displayNewsOnMainScreen = prefs.getBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen);
lockSoundChanges = prefs.getBoolean("lockSoundChanges", default_lockSoundChanges);
@ -256,6 +266,7 @@ public class Settings implements SharedPreferences
noticeAndroid10WifiShown = prefs.getBoolean("noticeAndroid10WifiShown", false);
lastNewsPolltime = prefs.getLong("lastNewsPolltime", default_lastNewsPolltime);
lastUpdateCheck = prefs.getLong("lastUpdateCheck", default_lastUpdateCheck);
String whbdString = prefs.getString("whatHasBeenDone", "");
if(whbdString != null && whbdString.length() > 0)
@ -432,9 +443,15 @@ public class Settings implements SharedPreferences
if(!prefs.contains("startScreen") | force)
editor.putString("startScreen", String.valueOf(default_startScreen));
if(!prefs.contains("tabsPlacement") | force)
editor.putString("tabsPlacement", String.valueOf(default_tabsPlacement));
if(!prefs.contains("executeRulesAndProfilesWithSingleClick") | force)
editor.putBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick);
if(!prefs.contains("automaticUpdateCheck") | force)
editor.putBoolean("automaticUpdateCheck", default_automaticUpdateCheck);
if(!prefs.contains("displayNewsOnMainScreen") | force)
editor.putBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen);
@ -447,6 +464,9 @@ public class Settings implements SharedPreferences
if(!prefs.contains("lastNewsPolltime") | force)
editor.putLong("lastNewsPolltime", default_lastNewsPolltime);
if(!prefs.contains("lastUpdateCheck") | force)
editor.putLong("lastUpdateCheck", default_lastUpdateCheck);
if(!prefs.contains("whatHasBeenDone") | force)
editor.putString("whatHasBeenDone", "");
@ -510,7 +530,9 @@ public class Settings implements SharedPreferences
editor.putString("activityDetectionRequiredProbability", String.valueOf(activityDetectionRequiredProbability));
editor.putBoolean("privacyLocationing", privacyLocationing);
editor.putString("startScreen", String.valueOf(startScreen));
editor.putString("tabsPlacement", String.valueOf(tabsPlacement));
editor.putBoolean("executeRulesAndProfilesWithSingleClick", executeRulesAndProfilesWithSingleClick);
editor.putBoolean("automaticUpdateCheck", automaticUpdateCheck);
editor.putBoolean("displayNewsOnMainScreen", displayNewsOnMainScreen);
editor.putBoolean("lockSoundChanges", lockSoundChanges);
@ -518,6 +540,7 @@ public class Settings implements SharedPreferences
editor.putBoolean("noticeAndroid10WifiShown", noticeAndroid10WifiShown);
editor.putLong("lastNewsPolltime", lastNewsPolltime);
editor.putLong("lastUpdateCheck", lastUpdateCheck);
editor.putString("whatHasBeenDone", Miscellaneous.explode(";", whatHasBeenDone));
@ -559,4 +582,4 @@ public class Settings implements SharedPreferences
return null;
}
}
}

View File

@ -77,10 +77,17 @@ public class Trigger
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;
@ -306,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));
@ -339,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
@ -572,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,6 +23,8 @@ import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import static com.jens.automation2.Trigger.triggerParameter2Split;
public class XmlFileInterface
{
public static String settingsFileName = "Automation_settings.xml";
@ -91,7 +93,7 @@ public class XmlFileInterface
{
//start a tag called "root"
serializer.startTag(null, "PointOfInterest");
//i indent code just to have a view similar to xml-tree
serializer.startTag(null, "name");
serializer.text(PointOfInterest.getPointOfInterestCollection().get(i).getName());
@ -253,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)
@ -273,6 +275,8 @@ public class XmlFileInterface
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");
}
@ -861,45 +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("notification"))
// newTrigger.setTriggerType(Trigger_Enum.notification);
// 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 if(triggerEventString.equals("notification"))
// newTrigger.setTriggerType(Trigger_Enum.notification);
else
newTrigger.setTriggerType(Trigger_Enum.valueOf(triggerEventString));
newTrigger.setTriggerType(Trigger_Enum.valueOf(triggerEventString));
}
else if (name.equals("TriggerParameter1"))
{
@ -922,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)
{
@ -969,6 +988,7 @@ public class XmlFileInterface
{
newTrigger.setActivityDetectionType(0);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.bluetoothConnection)
{
@ -978,6 +998,7 @@ public class XmlFileInterface
newTrigger.setBluetoothEvent(substrings[0]);
newTrigger.setBluetoothDeviceAddress(substrings[1]);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.headsetPlugged)
{
@ -989,9 +1010,10 @@ public class XmlFileInterface
{
newTrigger.setHeadphoneType(-1);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
newTrigger.setTriggerParameter2(triggerParameter2);
else
newTrigger.setTriggerParameter2(triggerParameter2);
}
else
{
@ -1079,17 +1101,6 @@ 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"))
@ -1113,29 +1124,7 @@ 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));
}
@ -1223,8 +1212,43 @@ public class XmlFileInterface
{
newAction.setParameter2(tag);
}
}
/*
androidx.security.crypto.MasterKey.Builder
MasterKey mainKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
*/
}
}
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

@ -144,8 +144,33 @@ public class LocationProvider
}
else
{
speedCalculation:
if (locationList.size() >= 2)
{
while (locationList.size() > 2)
{
// Remove all entries except for the last 2
Miscellaneous.logEvent("i", "Speed", "About to delete oldest position record until only 2 left. Currently have " + String.valueOf(locationList.size()) + " records.", 4);
locationList.remove(0);
}
/*
The two most recent locations in the list must have a usable accuracy.
*/
for(int i = 0; i < 2; i++)
{
if
(
(locationList.get(i).getProvider().equals(LocationManager.GPS_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyGps)
||
(locationList.get(i).getProvider().equals(LocationManager.NETWORK_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyNetwork)
)
{
Miscellaneous.logEvent("i", "Speed", "Not using 2 most recent locations for speed calculation because at least one does not have a satisfactory accuracy: " + locationList.get(i).toString(), 4);
break speedCalculation;
}
}
Miscellaneous.logEvent("i", "Speed", "Trying to calculate speed based on the last locations.", 4);
double currentSpeed;
@ -184,14 +209,6 @@ public class LocationProvider
}
else
Miscellaneous.logEvent("i", "Speed", "Last two locations are too far apart in terms of time. Cannot use them for speed calculation.", 4);
while (locationList.size() > 2)
{
// Remove all entries except for the last 2
Miscellaneous.logEvent("i", "Speed", "About to delete oldest position record until only 2 left. Currently have " + String.valueOf(locationList.size()) + " records.", 4);
locationList.remove(0);
}
}
else
{
@ -489,18 +506,10 @@ public class LocationProvider
{
// time is up, no cell location updates since x minutes, start accelerometer
String text = "Timer triggered. Based on the last location and speed we may be at a POI. Forcing location update in case CellLocationChangedReceiver didn\'t fire.";
// Miscellaneous.logEvent("i", "AccelerometerHandler", text, 5);
// CellLocationChangedReceiver.stopCellLocationChangedReceiver();
// startAccelerometerReceiver();
Location currentLocation = CellLocationChangedReceiver.getInstance().getLocation("coarse");
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false);
}
/*else if(msg.what == 0)
{
String text = "Abort command received, deactivating SpeedReceiver";
Miscellaneous.logEvent("i", "SpeedHandler", text, 4);
stopAccelerometerReceiver();
}*/
}
}
}

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

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

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,138 +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="@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">
<Button
android:id="@+id/bSelectApp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectApplication" />
<EditText
android:id="@+id/etSelectedApplication"
android:layout_width="wrap_content"
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="wrap_content"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000"
android:visibility="invisible" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:text="@string/startAppChoiceNote" />
</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="wrap_content"
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:layout_width="match_parent"
android:layout_height="115dp" >
</ListView>
<Button
android:id="@+id/bSaveActionStartOtherActivity"
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,111 @@
<?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" />
<TextView
android:id="@+id/tvAppVersion"
android:layout_marginTop="@dimen/default_margin"
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

@ -27,7 +27,7 @@
<EditText
android:id="@+id/etSelectedSoundFile"
android:layout_marginVertical="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button

View File

@ -2,7 +2,7 @@
<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"

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,47 @@
<?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">
<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/vibrate" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/VibrateExplanation" />
<EditText
android:id="@+id/etVibratePattern"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/default_margin"/>
<Button
android:id="@+id/bTestVibratePattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/default_margin"
android:text="@string/test" />
<Button
android:id="@+id/bSaveVibratePattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</LinearLayout>
</ScrollView>

View File

@ -4,6 +4,15 @@
android:layout_height="match_parent"
android:layout_margin="@dimen/default_margin">
<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/setScreenBrightness" />
<CheckBox
android:id="@+id/chkAutoBrightness"
android:layout_width="match_parent"

View File

@ -1,7 +1,8 @@
<?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="match_parent" >
android:layout_height="match_parent"
android:layout_margin="@dimen/default_margin">
<LinearLayout
android:layout_width="match_parent"
@ -9,7 +10,6 @@
android:orientation="vertical" >
<LinearLayout
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -92,7 +92,7 @@
<Button
android:id="@+id/bSaveBluetoothTrigger"
android:layout_marginTop="10dp"
android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />

View File

@ -3,7 +3,8 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
android:orientation="vertical"
android:layout_margin="@dimen/default_margin" >
<LinearLayout
android:layout_width="match_parent"

View File

@ -2,10 +2,9 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp" >
android:layout_margin="@dimen/default_margin" >
<LinearLayout
android:layout_margin="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
@ -25,7 +24,9 @@
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:shrinkColumns="1"
android:stretchColumns="1">
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
@ -54,13 +55,13 @@
android:text="@string/application" />
<LinearLayout
android:orientation="horizontal"
android:orientation="vertical"
android:layout_marginHorizontal="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/etSelectedApplication"
android:layout_marginHorizontal="@dimen/default_margin"
android:id="@+id/etActivityOrActionPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/anyApp"
@ -68,6 +69,7 @@
<Button
android:id="@+id/bSelectApp"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectApplication" />

View File

@ -0,0 +1,194 @@
<?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:drawableLeft="@drawable/contacts"
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

@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_weight="30"
android:layout_margin="10dp" >
android:layout_margin="@dimen/default_margin" >
<LinearLayout
android:layout_width="match_parent"
@ -34,18 +34,18 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/insideOrOutsideTimeFrames"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/insideOrOutsideTimeFrames"
android:textAppearance="?android:attr/textAppearanceLarge" />
<RadioGroup
android:layout_width="match_parent"
@ -65,18 +65,18 @@
android:text="@string/leaving" />
</RadioGroup>
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<TextView
android:id="@+id/tvCurrentNfcIdValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/timeFrameWhichDays"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/tvCurrentNfcIdValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/timeFrameWhichDays"
android:textAppearance="?android:attr/textAppearanceLarge" />
<CheckBox
android:id="@+id/checkMonday"
@ -122,6 +122,7 @@
<Button
android:id="@+id/bSaveTimeFrame"
android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />

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

@ -52,10 +52,23 @@
android:entries="@array/startScreenOptions"
android:entryValues="@array/startScreenOptionsValues" />
<ListPreference
android:key="tabsPlacement"
android:title="@string/tabsPlacement"
android:summary="@string/tabsPlacementSummary"
android:entries="@array/tabsPlacementOptions"
android:entryValues="@array/tabsPlacementOptionsValues" />
<CheckBoxPreference
android:key="executeRulesAndProfilesWithSingleClick"
android:title="@string/executeRulesAndProfilesWithSingleClickTitle" />
<CheckBoxPreference
android:key="automaticUpdateCheck"
android:enabled="false"
android:title="@string/automaticUpdateCheck"
android:summary="@string/automaticUpdateCheckSummary"/>
<CheckBoxPreference
android:key="displayNewsOnMainScreen"
android:title="@string/displayNewsOnMainScreen"

View File

@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="10dp" >
android:layout_margin="@dimen/default_margin" >
<TextView
android:id="@+id/tvVolumeTestExplanation"

View File

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

View File

@ -302,87 +302,38 @@
</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>
<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" />
<Button
android:id="@+id/bShareConfigAndLog"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/shareConfigAndLogFilesWithDev" />
</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 +0,0 @@
<?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">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
</TabHost>

View File

@ -0,0 +1,27 @@
<?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="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TabWidget
android:id="@android:id/tabs"
android:layout_weight="0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</LinearLayout>
</TabHost>

View File

@ -0,0 +1,27 @@
<?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="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_weight="0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</TabHost>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp">

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,15 @@
<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="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="distanceBetween">Der Abstand zwischen GPS- und Mobilfunk-Position beträgt %1$d m. Dies +1 sollte der minimale Radius sein.</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 +57,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>
@ -123,14 +107,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 +132,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>
@ -190,38 +174,13 @@
<string name="serviceNotRunning">Dienst läuft nicht.</string>
<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>
<string name="failedToTriggerBluetooth">Fehler beim Ändern des Bluetooth Status. Hat dieses Gerät Bluetooth?</string>
<string name="logAttemptingDownloadOf">Versuche Download von</string>
<string name="logErrorGettingConnectionManagerService">Fehler beim Anbinden an den connectionManager Dienst. Ändere nichts am Usb-Router Status.</string>
<string name="logErrorDeterminingCurrentUsbTetheringState">Fehler beim Aufrufen des gegenwärtigen USB-Router Status.</string>
<string name="logDetectingTetherableUsbInterface">Überprüfe routingfähiges Interface.</string>
<string name="logClearingBothLocationListeners">Stoppe beide LocationListener.</string>
<string name="logStartingServiceAfterAppUpdate">Starte Dienst nach Programm Aktualisierung. Er lief vor der Aktualisierung schon.</string>
<string name="logNotStartingServiceAfterAppUpdate">Starte Dienst nicht automatisch nach Programm Aktualisierung. Er lief davor nicht.</string>
<string name="logStartingServiceAtPhoneBoot">Starte Dienst nach Neustart des Telefons.</string>
<string name="logNotStartingServiceAtPhoneBoot">Starte Dienst nicht automatisch nach Neustart des Telefons.</string>
<string name="applicationHasBeenUpdated">Die Anwendung wurde aktualisiert.</string>
<string name="startServiceAfterAppUpdate">Dienst nach Anwendungsupdate automatisch wieder starten, wenn er vorher lief.</string>
<string name="startServiceAfterAppUpdateShort">Starte Dienst nach Update</string>
<string name="wifiConnection">WLAN Verbindung</string>
<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 +203,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 +212,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">Anwendung wählen</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 +220,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,15 +239,10 @@
<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>
<string name="deactivate">Deaktivieren</string>
<string name="airplaneModeSdk17Warning">Seit Android Version 4.2 funktioniert diese Funktion - wenn überhaupt - nur noch mit gerooteten Geräten.</string>
<string name="triggerUrlReplacementPositionError">Laut Ihren Einstellungen sollte der aufzurufenden Adresse eine Position hinzugefügt werden. Leider ist der im Moment nicht möglich, daß noch keine Position bekannt ist.</string>
<string name="addIntentValue">Parameter-Paar hinzufügen</string>
<string name="parameterName">Parameter Name</string>
<string name="parameterValue">Parameter Wert</string>
@ -342,7 +271,7 @@
<string name="with">mit</string>
<string name="phoneNumber">Telefonnummer</string>
<string name="enterPhoneNumber">Geben Sie eine Telefonnummer ein. Leer lassen für irgendeine Nummer.</string>
<string name="phoneDirection">Wählen Sie die Gesprächsrichtung</string>
<string name="phoneDirection">Wählen Sie die\nGesprächsrichtung</string>
<string name="any">egal</string>
<string name="incoming">eingehend</string>
<string name="outgoing">ausgehend</string>
@ -366,7 +295,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>
@ -381,30 +309,24 @@
<string name="toggling">Schalte um</string>
<string name="toggle">umzuschalten</string>
<string name="overlapBetweenPois">Überschneidung mit Ort %1$s von %2$s Metern festgestellt. Reduzieren Sie den Radius um mindestens diesen Wert.</string>
<string name="noOverLap">Keine Überschneidung mit anderen Orten festgestellt.</string>
<string name="ruleToggable">Regel %1$s ist umkehrbar.</string>
<string name="ruleNotToggable">Regel %1$s ist nicht umkehrbar.</string>
<string name="none">keiner</string>
<string name="anyLocation">irgendein Ort</string>
<string name="invalidPoiName">Ungültiger Name für einen Ort.</string>
<string name="eraseSettings">Einstellungen löschen</string>
<string name="defaultSettings">Standard Einstellungen</string>
<string name="areYouSure">Sind sie sicher?</string>
<string name="poiCouldBeInRange">Mindestens Ort %1$s könnte im nahen Umkreis liegen, wenn nicht noch andere.</string>
<string name="noPoiInRelevantRange">Kein Ort im näheren Umkreis.</string>
<string name="activityDetection">Aktivitätserkennung</string>
<string name="detectedActivity">Erkannte Tätigkeit:</string>
<string name="detectedActivityInVehicle">In einem Fahrzeug (Auto/Bus)</string>
<string name="detectedActivityOnBicycle">Auf dem Fahrrad</string>
<string name="detectedActivityOnFoot">Zu Fuß</string>
<string name="detectedActivityStill">Ruhe</string>
<string name="detectedActivityUnknown">Unbekannt</string>
<string name="detectedActivityUnknown">unbekannt</string>
<string name="detectedActivityTilting">Kippen</string>
<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 +334,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>
@ -424,9 +346,7 @@
<string name="bluetoothDeviceInRange">Bluetooth Gerät %1$s in Reichweite.</string>
<string name="bluetoothDeviceOutOfRange">Bluetooth Gerät %1$s außer Reichweite.</string>
<string name="anyDevice">irgendeinem Gerät</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceName">Regel trifft nicht zu. Nicht der korrekte Bluetooth-Geräte-Name.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceAddress">Regel trifft nicht zu. Nicht die korrekte Bluetooth-Geräte-Adresse.</string>
<string name="noDevice">kein Gerät</string>
<string name="noDevice">kein Gerät</string>
<string name="selectDeviceFromList">Gerät aus Liste</string>
<string name="connectionToDevice">Gerät verbunden</string>
<string name="disconnectionFromDevice">Gerät getrennt</string>
@ -434,9 +354,7 @@
<string name="deviceOutOfRange">Gerät außer Reichweite</string>
<string name="selectDeviceOption">Wählen Sie eine Geräteoption.</string>
<string name="selectConnectionOption">Wählen Sie eine Verbindungsoption.</string>
<string name="ruleDoesntApplyDeviceInRangeButShouldNotBe">Regel trifft nicht zu. Gerät ist in Reichweite, aber sollte nicht sein.</string>
<string name="ruleDoesntApplyStateNotCorrect">Regel trifft nicht zu. Falscher Status.</string>
<string name="triggerHeadsetPlugged">Headset Verbindung</string>
<string name="triggerHeadsetPlugged">Headset Verbindung</string>
<string name="actionPlayMusic">Musikplayer öffnen</string>
<string name="headsetConnected">Headset (type: %1$s) verbunden</string>
<string name="headsetDisconnected">Headset (type: %1$s) getrennt</string>
@ -444,28 +362,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>
@ -482,7 +389,7 @@
<string name="soundModeNormal">Normal</string>
<string name="soundModeVibrate">Vibration</string>
<string name="soundModeSilent">Stumm</string>
<string name="enterAname">Enter a name!</string>
<string name="enterAname">Enter a name.</string>
<string name="noChangeSelectedProfileDoesntMakeSense">Keine Veränderungen ausgewählt. Profil macht so keinen Sinn.</string>
<string name="noProfilesCreateOneFirst">Es existieren keine Profile. Erstellen Sie erst eins.</string>
<string name="errorActivatingProfile">Fehler beim Aktivieren des Profils:</string>
@ -506,10 +413,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>
@ -533,7 +436,6 @@
<string name="appRequiresPermissiontoAccessExternalStorage">Automation benötigt Rechte, um auf den Speicher zuzugreifen, um seine Konfiguration und Regeln lesen zu können.</string>
<string name="mainScreenPermissionNote">Automation benötigt mehr Rechte, um vollständig funktionsfähig zu sein. Klicken Sie auf diesen Text, um mehr zu erfahren und die fehlenden Rechte zu beantragen.</string>
<string name="invalidDevice">Ungültiges Gerät</string>
<string name="google_app_id">your app id</string>
<string name="logFileMaxSizeSummary">Maximale Größe der Protokolldatei in Megabyte. Wenn sie größer wird, wird sie rotiert.</string>
<string name="logFileMaxSizeTitle">Maximale Größe des Protokolls [Mb]</string>
<string name="android.permission.READ_CALL_LOG">Telefonprotokoll lesen</string>
@ -595,9 +497,9 @@
<string name="manageLocations">Orte anlegen oder ändern</string>
<string name="publishedOn">veröffentlicht am</string>
<string name="filesHaveBeenMovedTo">Automation benutzt jetzt ein anderes Verzeichnis, um Ihre Daten zu speichern. Alle Ihre Automation-Dateien wurden hierhin verschoben: \"%s\". Die Berechtigung für den externen Speicher wird nun nicht mehr benötigt; Sie können Sie entfernen. In einer künftigen Version wird sie entfernt werden.</string>
<string name="locationEngineDisabledShort">Die Position kann nicht mehr bestimmt werden.</string>
<string name="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</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>
@ -633,5 +535,63 @@
<string name="shareConfigAndLogFilesWithDev">Konfigurations- und Logdatei mit Entwickler teilen (via email).</string>
<string name="shareConfigAndLogExplanation">Dies wird eine neue Email öffnen mit Konfigurations- und Logdateien als Zip-Anhang. Sie wird nicht automatisch versendet. D.h. Sie können so z.B. auch den Adressaten zu sich selbst ändern.</string>
<string name="notificationTriggerExplanation">Dieser Auslöser reagiert auf Benachrichtigungen anderer Anwendung im Benachrichtigungsbereich von Android (oder wenn diese geschlossen werden). Sie können eine bestimmte Anwendung festlegen, von die Nachricht stammen muß. Wenn nicht, zählt jede Benachrichtigung. Sie können auch Zeichenketten für Titel oder Nachrichteninhalt festlegen, die enthalten sein müssen. Die Groß-/Kleinschreibung wird hierbei nicht berücksichtigt.</string>
<string name="ruleActivationComplete">Regel \"%1$s\" wurde fertig ausgeführt.</string>
<string name="addParameters">Parameter hinzufügen</string>
<string name="errorRunningRule">Fehler beim Ausführen einer Regel.</string>
<string name="startAppChoiceNote">Hier haben Sie 2 grundsätzliche Optionen:\n\n1. Sie können ein Programm starten, indem Sie eine Activity auswählen.\nStellen Sie sich das so vor, daß Sie ein bestimmtes Fenster einer Anwendung vorauswählen, in das man direkt springt. Behalten Sie im Kopf, daß das nicht immer funktionieren wird. Das liegt daran, daß die Fenster einer Anwendung miteinander interagieren können, sich u.U. Parameter übergeben. Wenn man jetzt ganz kalt in ein bestimmtes Fenster springt, könnte dieses zum Start z.B. bestimmte Parameter erwarten - die fehlen. So könnte es passieren, daß das Fenster zwar versucht zu öffnen, das aber nicht klappt und es somit nie wirlich sichtbar wird. Versuchen Sie\'s trotzdem!\nSie können den Pfad manuell eingeben, sollten aber den Auswählen-Knopf benutzen. Wenn Sie es dennoch manuell eingeben, geben Sie den PackageName ins obere Feld ein und den vollen Pfad der Activity ins untere.\n\n2. Auswahl per Action\nIm Gegensatz zur Auswahl eines bestimmten Fensters, können Sie ein Programm auch über eine Action starten lassen. Stellen Sie sich das so vor als würden Sie in den Wald rufen \"Ich hätte gerne XYZ\" und falls eine Anwendung installiert ist, die das liefern kann, wird sie gestartet. Ein gutes Beispiel wäre zum Beispiel "Browser starten" - es könnten sogar mehrere installiert sein, die das können (aber normalerweise gibts eine, die als Standard eingestellt ist).\nDiese Action müssen Sie manuell eingeben. Der PackageName ist hier optional. Behalten Sie dabei im Auge, daß mögliche Variablen nicht aufgelöst werden. Beispielsweise werden Sie häufig im Internet finden, daß man die Kamera über die Action \"MediaStore.ACTION_IMAGE_CAPTURE\" starten kann. Das ist grundsätzlich nicht richtig, wird aber nicht direkt funktionieren, denn das ist nur eine Variable. Sie müssen dann einen Blick in die Android Dokumentation werfen, wo Sie sehen werden, daß sich hinter dieser Variable eigentlich der Wert \"android.media.action.IMAGE_CAPTURE\" verbirgt. Gibt man diesen in das Feld ein, wird\'s funktionieren.</string>
<string name="cantFindSoundFile">Kann die Audiodatei %1$s nicht finden und daher auch nicht abspielen.</string>
<string name="startAppByActivity">per Activity</string>
<string name="startAppByAction">per Action</string>
<string name="startAppSelectionType">Auswahlmethode</string>
<string name="com.wireguard.android.permission.CONTROL_TUNNELS">Tunnelverbindungen der Wireguard Anwendung steuern</string>
<string name="enterPackageName">Geben Sie einen gültigen Paketnamen ein.</string>
<string name="configurationExportedSuccessfully">Konfiguration erfolgreich exportiert.</string>
<string name="ConfigurationExportError">Beim Exportieren der Konfiguration ist ein Fehler aufgetreten.</string>
<string name="configurationImportedSuccessfully">Konfiguration erfolgreich importiert.</string>
<string name="enterValidAction">Geben Sie eine gültige Action ein.</string>
<string name="exportConfiguration">Konfiguration exportieren</string>
<string name="importConfiguration">Konfiguration importieren</string>
<string name="moreSettings">Mehr Einstellungen</string>
<string name="importExportExplanation">Wenn Sie auf im- oder exportieren klicken, müssen Sie im nächsten Schritt das Verzeichnis auswählen, in das Dateien exportiert oder von dem Dateien importiert werden. Im Falle des Exports können vorhandene Dateien in diesem Ordner überschrieben werden.</string>
<string name="intentDataComment">Wenn Ihr Parameter vom Typ \"Uri\" ist und Sie \"IntentName\" als Name angeben (Groß-/Kleinschreibung ist irrelevant), wird der Parameter nicht als normaler Parameter mit putExtra() angehängt, sondern wird stattdessen mit setData() angehängt.</string>
<string name="noApplicableFilesFoundInDirectory">Keine passenden Dateien im Ordner gefunden.</string>
<string name="noFilesImported">Keine Dateien konnten importiert werden.</string>
<string name="notAllFilesImported">Nicht alle passenden Dateien konnten importiert werden.</string>
<string name="openExamplesPage">Webseite mit Beispielen öffnen</string>
<string name="phoneNumberExplanation">Sie können eine bestimmte Nummer eingeben, aber müssen nicht. Wenn Sie eine angeben wollen, können Sie auch eine aus dem Adressbuch auswählen. 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>
<string name="clone">Klonen</string>
<string name="updateAvailable">Es gibt eine neue Version der Anwendung. Möchten Sie den Browser öffnen, um sie herunterzuladen?</string>
<string name="automaticUpdateCheckSummary">Nur bei der APK Version.</string>
<string name="automaticUpdateCheck">Nach Updates suchen</string>
<string name="locationFound">Position gefunden. Der vorgeschlagene Mindestradius für Orte beträgt %1$d m.</string>
<string name="locationFoundInaccurate">Es konnte nur eine ungenaue Position gefunden werden. Das funktioniert u.U. nicht zuverlässig. Der vorgeschlagene Mindestradius für Orte beträgt %1$d m.</string>
<string name="pleaseGiveBgLocation">Gehen Sie auf dem nächsten Bildschirm bitte auf Berechtigungen, dann Position. Wählen Sie dort Immer erlauben aus, um Automation zu ermöglichen, die Position im Hintergrund zu ermitteln.</string>
<string name="noLocationCouldBeFound">Leider konnte nach einem Limit von %1$s Sekunden keine Position gefunden werden.</string>
<string name="vibrate">Vibrieren</string>
<string name="test">Ausprobieren</string>
<string name="VibrateExplanation">Geben Sie eine Vibrationsdauer, gefolgt von einem Komma ein, dann eine Pausendauer. Sie können so viele Vibrationen eingeben, wie sie möchten. Trennen Sie sie wieder mit Kommata.\nZ.B. wird das Muster 100,500,500,1000,100 100ms vibrieren, 500ms warten, 500ms vibrieren, 1000ms warten, 100ms vibrieren.\nWenn Sie denken, daß eine Vibration verschluckt wird, verlängern Sie die Pause vor ihr.</string>
<string name="pleaseEnterValidVibrationPattern">Bitte geben Sie ein gültiges Vibrationsmuster ein.</string>
<string name="tabsPlacement">Position der Tableiste</string>
<string name="top">Oben</string>
<string name="bottom">Unten</string>
<string name="tabsPlacementSummary">Wol soll die Taskleiste angezeigt werden?</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">C\'è stato un errore durante l\'esportazione della configurazione.</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="actionTurnBluetoothOff">attiva Bluetooth</string>
<string name="actionTurnBluetoothOn">disattiva il Bluetooth</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">disattiva Bluetooth</string>
<string name="actionTurnBluetoothOn">attiva 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="addRule">Aggiungi regola</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">Ignora 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,191 @@
<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.READ_EXTERNAL_STORAGE">Legge la memoria esterna</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">Leggere 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.WAKE_LOCK">Mantiene attivo il dispositivo</string>
<string name="android.permission.WRITE_EXTERNAL_STORAGE">Scrive su SD</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">Mantienere attivo il dispositivo</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="featuresDisabled">L\'applicazione è in esecuzione in modalità limitata a causa di autorizzazioni mancanti.</string>
<string name="appStarted">App avviata.</string>
<string name="appStopped">App terminata.</string>
<string name="app_name">Automation</string>
<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="comparing">Ho la posizione sia dalla rete che dal GPS e le sto confrontando...</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">Controllare i tunnels dell\'applicazione wireguard</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="enterAPositiveValidNonDecimalNumber">Inserisci un numero intero positivo</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="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="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="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="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="google_app_id">id della tua app</string>
<string name="logGotGpsUpdate">GPS aggiornato. Precisione:</string>
<string name="logGotNetworkUpdate">Rete aggiornata. Precisione:</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 nostra privacy, nelle regole che possano inviare SMS o coinvolgere lo stato del telefono, sono state rimossi eventi ed azioni rilevanti.</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="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 sensore ha bisogno di molte risorse di sistema. 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 +261,283 @@ 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="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 c\'è nessun profilo nella tua configurazione. Prima di tutto, creane 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 si sta eseguendo 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="phoneDirection">Seleziona se\nentrante o uscente</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="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="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="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="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="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="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:\n\n1. 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\n\ninterazione 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.\n\n2. 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="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="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.</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 +545,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 +571,19 @@ 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="wifiNameSpecifiedCheckingThat">Verifica della wifi indicata.</string>
<string name="wifiState">Stato Wifi</string>
<string name="with">con</string>
<string name="withLabel">con etichetta</string>
<string name="writeLogFile">Memorizza un file di log</string>
<string name="writingSettingsToPersistentMemory">Scrivo le impostazioni nella memoria di massa.</string>
<string name="yes">Si</string>
<string name="edit">Elaborare</string>
</resources>
<string name="clone">Clonare</string>
<string name="state">Status</string>
<string name="urlToTriggerExplanation">Questa funzione NON apre un navigatore, ma attiva un indirizzo URL in secondo piano. Puoi usarla per esempio per mandare dei comandi al tuo sistema di domotica.</string>
<string name="automaticUpdateCheck">Controlla aggiornamenti</string>
<string name="automaticUpdateCheckSummary">Applicabile solo alla versione APK.</string>
<string name="updateAvailable">C\'è un nuovo aggiornamento disponibile. Vuoi aprire il navigatore per scaricarlo?</string>
<string name="locationFound">Ubicazione trovata. Il raggio minimo per le ubicazioni è di %1$d m.</string>
<string name="locationFoundInaccurate">È stato possibile solo trovare una ubicazione con una precisione limitata. Potrebbe non funzionare in maniera affidabile. Il raggio minimo per le ubicazioni è di %1$d m.</string>
<string name="noLocationCouldBeFound">Nessuna posizione è stata trovata dopo un tempo di attesa di %1$s seconds.</string>
<string name="pleaseGiveBgLocation">Nella schermata successiva vai su permessi, poi posizione. Lì seleziona \"Consenti sempre\" per permettere ad Automation di determinare la tua posizione in secondo piano.</string>
</resources>

View File

@ -10,10 +10,10 @@
</string-array>
<string-array name="startScreenOptions">
<item name="0">Home</item>
<item name="1">Locations</item>
<item name="2">Rules</item>
<item name="3">Profiles</item>
<item name="0">@string/overview</item>
<item name="1">@string/pois</item>
<item name="2">@string/rules</item>
<item name="3">@string/profiles</item>
</string-array>
<string-array name="startScreenOptionsValues">
<item name="0">0</item>
@ -21,4 +21,16 @@
<item name="2">2</item>
<item name="3">3</item>
</string-array>
<string-array name="tabsPlacementOptions">
<item name="0">@string/top</item>
<item name="1">@string/bottom</item>
</string-array>
<string-array name="tabsPlacementOptionsValues">
<item name="0">0</item>
<item name="1">1</item>
<item name="2">2</item>
<item name="3">3</item>
</string-array>
</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="comparing">Have both network and gps location. Comparing...</string>
<string name="logNoSuitableProvider">No suitable location providers could be used.</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" translatable="false">Have both network and gps location. Comparing...</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>
@ -110,8 +107,8 @@
<string name="wifiState">Wifi state</string>
<string name="listenToAccelerometerState">Observe device movement where wifi is not available</string>
<string name="accelerometer">Accelerometer</string>
<string name="accelerometerTimer">Use Accelerometer after x minutes without cell mast change</string>
<string name="cellMastIdleTime">Cell mast idle time</string>
<string name="accelerometerTimer">Use Accelerometer after x minutes without tower mast change</string>
<string name="cellMastIdleTime">Cell tower idle time</string>
<string name="accelerometerThresholdDescription">Threshold for accelerometer movements</string>
<string name="accelerometerThreshold">Accelerometer threshold</string>
<string name="positioningThresholds">Positioning thresholds</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="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>
@ -264,36 +261,36 @@
<string name="anotherAppIsRunning">Another app is started/stopped</string>
<string name="settingsCategoryProcessMonitoring">Process monitoring</string>
<string name="timeBetweenProcessMonitoringsTitle">Seconds between process monitorings</string>
<string name="timeBetweenProcessMonitoringsSummary">The lower the higher the battery usage</string>
<string name="refreshingProcessList">Refreshing process list.</string>
<string name="timeBetweenProcessMonitoringsSummary">The lower, the higher the battery usage</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>
@ -342,7 +339,7 @@
<string name="with">with</string>
<string name="phoneNumber">Phone number</string>
<string name="enterPhoneNumber">Enter phone number. Leave empty for any number.</string>
<string name="phoneDirection">Select call direction</string>
<string name="phoneDirection">Select call\ndirection</string>
<string name="any">any</string>
<string name="incoming">incoming</string>
<string name="outgoing">outgoing</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>
@ -381,17 +378,17 @@
<string name="toggling">Toggling</string>
<string name="toggle">toggle</string>
<string name="overlapBetweenPois">Overlap detected to location %1$s of %2$s meters. Reduce radius by at least that.</string>
<string name="noOverLap">No overlap to other locations detected.</string>
<string name="ruleToggable">Rule %1$s is toggable.</string>
<string name="ruleNotToggable">Rule %1$s is not suitable for toggling.</string>
<string name="noOverLap" translatable="false">No overlap to other locations detected.</string>
<string name="ruleToggable" translatable="false">Rule %1$s is toggable.</string>
<string name="ruleNotToggable" translatable="false">Rule %1$s is not suitable for toggling.</string>
<string name="none">none</string>
<string name="anyLocation">any location</string>
<string name="invalidPoiName">Invalid name for location.</string>
<string name="eraseSettings">Erase settings</string>
<string name="defaultSettings">Default settings</string>
<string name="areYouSure">Are you sure?</string>
<string name="poiCouldBeInRange">At least location %1$s could be in range, if not others in addition.</string>
<string name="noPoiInRelevantRange">No location in relevant range.</string>
<string name="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>
@ -399,13 +396,13 @@
<string name="detectedActivityOnBicycle">On bicycle</string>
<string name="detectedActivityOnFoot">On foot</string>
<string name="detectedActivityStill">Still</string>
<string name="detectedActivityUnknown">Unknown</string>
<string name="detectedActivityUnknown">unknown</string>
<string name="detectedActivityTilting">Tilting</string>
<string name="detectedActivityWalking">Walking</string>
<string name="detectedActivityRunning">Running</string>
<string name="detectedActivityInvalidStatus">Invalid activity</string>
<string name="ruleDoesntApplyActivityGivenButTooLowProbability">Rule doesn\'t apply. Detected activity %1$s given, but too low probability (%2$s %%), required %3$s %%.</string>
<string name="ruleDoesntApplyActivityNotPresent">Rule doesn\'t apply. Required activity %1$s not present.</string>
<string name="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>
@ -425,8 +422,8 @@
<string name="bluetoothDeviceInRange">Bluetooth device %1$s in range.</string>
<string name="bluetoothDeviceOutOfRange">Bluetooth device %1$s out of range.</string>
<string name="anyDevice">any device</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceName">Rule doesn\'t apply. Not the correct bluetooth device name.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceAddress">Rule doesn\'t apply. Not the correct bluetooth device address.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceName" translatable="false">Rule doesn\'t apply. Not the correct bluetooth device name.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceAddress" translatable="false">Rule doesn\'t apply. Not the correct bluetooth device address.</string>
<string name="noDevice">no device</string>
<string name="selectDeviceFromList">one from list</string>
<string name="connectionToDevice">connection to device</string>
@ -435,8 +432,8 @@
<string name="deviceOutOfRange">device out of range</string>
<string name="selectDeviceOption">Select a device option.</string>
<string name="selectConnectionOption">Select a connection option.</string>
<string name="ruleDoesntApplyDeviceInRangeButShouldNotBe">Rule doesn\'t apply. Device is in range, but should not be.</string>
<string name="ruleDoesntApplyStateNotCorrect">Rule doesn\'t apply. Wrong state.</string>
<string name="ruleDoesntApplyDeviceInRangeButShouldNotBe" translatable="false">Rule doesn\'t apply. Device is in range, but should not be.</string>
<string name="ruleDoesntApplyStateNotCorrect" translatable="false">Rule doesn\'t apply. Wrong state.</string>
<string name="triggerHeadsetPlugged">Headset connection</string>
<string name="actionPlayMusic">Open music player</string>
<string name="headsetConnected">Headset (type: %1$s) connected</string>
@ -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 tower 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>
@ -483,7 +479,7 @@
<string name="soundModeNormal">Normal</string>
<string name="soundModeVibrate">Vibrate</string>
<string name="soundModeSilent">Silent</string>
<string name="enterAname">Enter a name!</string>
<string name="enterAname">Enter a name.</string>
<string name="noChangeSelectedProfileDoesntMakeSense">No change selected. Profile doesn\'t make sense.</string>
<string name="noProfilesCreateOneFirst">There are no profiles in your configuration. Create one first.</string>
<string name="errorActivatingProfile">Error activating profile:</string>
@ -492,7 +488,7 @@
<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. You could use wifi tethering instead instead.</string>
<string name="usingNewThreadForRuleExecution">Using new thread for rule activation.</string>
<string name="startNewThreadForRuleExecution">Start new thread for rule activation.</string>
<string name="newThreadRules">New thread</string>
@ -501,16 +497,16 @@
<string name="ruleHistory">Rule history (most recent first):</string>
<string name="someOptionsNotAvailableYet">Some options are disabled as they cannot be used, yet. They will be introduced in a later program version.</string>
<string name="lockSoundChanges">Lock sound changes</string>
<string name="noProfileChangeSoundLocked">Profile will not be activated. Last activated profile was locked.</string>
<string name="noProfileChangeSoundLocked">Profile will not be activated. Last activated profile has been locked.</string>
<string name="currentVolume">Current volume</string>
<string name="enterValidReferenceValue">Enter a valid reference value.</string>
<string name="volumeTest">Volume test</string>
<string name="volumeTesterExplanation">To calculate a dB value for noise monitoring you need to specify a so called physical reference value. Please read Wikipedia for further information. This value is most likely different for every phone. Drag the seekbar to change the defined physical reference value. The higher the reference value the lower the dB value will be. Constant measurings will be performed every %1$s seconds and the results displayed below. Press back when you have found a suitable value.</string>
<string name="settingsWillTakeTime">Some settings will not be applied before certain environment settings change or service is restarted.</string>
<string name="phoneIsRooted">Phone is rooted.</string>
<string name="phoneIsNotRooted">Phone is not rooted.</string>
<string name="dataConWithRootSuccess">Data connection was successfully changed using superuser permissions.</string>
<string name="dataConWithRootFail">Data could not be changed using superuser permissions.</string>
<string name="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>
@ -534,7 +530,7 @@
<string name="appRequiresPermissiontoAccessExternalStorage">Automation requires access to external storage to read its settings and rules.</string>
<string name="mainScreenPermissionNote">Automation requires more permissions to fully function. Click on this text to find out more and request them.</string>
<string name="invalidDevice">Invalid device</string>
<string name="google_app_id">your app id</string>
<string name="google_app_id" translatable="false">your app id</string>
<string name="logFileMaxSizeSummary">Maximum log file size in Megabyte. Will be rotated if bigger.</string>
<string name="logFileMaxSizeTitle">Maximum log file size [Mb]</string>
<string name="android.permission.READ_CALL_LOG">Read phone log</string>
@ -568,7 +564,7 @@
<string name="android.permission.ACCESS_NOTIFICATION_POLICY">Override do not disturb policy</string>
<string name="theseAreThePermissionsRequired">These are the permissions required:</string>
<string name="ruleXrequiresThis">Rule \"%1$s\" requires this.</string>
<string name="helpTextActivityDetection">This feature can detect if you\'re currently on the go and if it is on foot or in which type of vehicle (to a certain extent). The feature is not fully built into Automation, but is provided by Google Play Services. Technically it does not give a yes/no result, but return a percentage to which level it is sure it detected you\'re status. You can setup the percentage value from which Automation will accept a result. Two remarks: 1) More than 1 status could occur at the same time. For example you might be WALKING inside a driving bus. 2) This sensor is relative cost intensive. If it is possible you might consider using alternatives, e.g. require your car\'s handsfree device to be connected to detect you\'re driving.</string>
<string name="helpTextActivityDetection">This feature can detect if you\'re currently on the go and if it is on foot or in which type of vehicle (to a certain extent). The feature is not fully built into Automation, but is provided by Google Play Services. Technically it does not give a yes/no result, but return a percentage to which level it is sure it detected you\'re status. You can setup the percentage value from which Automation will accept a result. Two remarks: 1) More than 1 status could occur at the same time. For example you might be WALKING inside a driving bus. 2) This sensor is relatively expensive in terms of battery usage. If it is possible you might consider using alternatives, e.g. require your car\'s handsfree device to be connected to detect you\'re driving.</string>
<string name="sendTextMessage">Send text message</string>
<string name="textToSend">Text to send</string>
<string name="textMessageAnnotations">You can directly enter a phone number. Alternatively use the contacts option to pick one. But keep in mind: The number will be stored here, not the contact. If you change the phone number of a selected contact you\'ll need to update this rule. It doesn\'t do that by itself.</string>
@ -579,9 +575,9 @@
<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="googleSarcasm">Thanks to Google\'s infinite wisdom and constant endeavor to protect everyone\'s privacy, all the rules that may be used to send sms or read the phone state have been stripped off of all the triggers and actions which Google considers risky.</string>
<string name="startAutomationAsService">Start automation as a service</string>
<string name="setScreenBrightness">Set screen brightness</string>
<string name="setScreenBrightnessEnterValue">Enter the desired brightness (from 0 to 100).</string>
@ -592,7 +588,7 @@
<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 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>
@ -606,11 +602,10 @@
<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 anymore. Click here to find out why.</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 with a rooted device or a debug version. If you want to send me or yourself the config- and logfiles you can use the below button.</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. FOR 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>
@ -635,7 +630,64 @@
<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">You can enter an activity path manually, but it\'s recommended to use the \"Select\" button.\nIf you choose to enter something manually keep in mind no variables will be resolved. If you want to start the camera for example \"MediaStore.ACTION_IMAGE_CAPTURE\" will not work. You have to look at the Android documentation and use its value instead which would be \"android.media.action.IMAGE_CAPTURE\".</string>
<string name="startAppChoiceNote">Here you have 2 general options:\n\n1. 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.\n\n2. 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>
<string name="automaticUpdateCheck">Check for updates</string>
<string name="automaticUpdateCheckSummary">Only applies to APK version.</string>
<string name="updateAvailable">There\'s a new update available. Would you like opening the browser to download it?</string>
<string name="locationFound">Location found. The suggested minimum radius for locations is %1$d m.</string>
<string name="locationFoundInaccurate">Only a location with a limited accuracy could be found. It might not work reliably. The suggested minimum radius for locations is %1$d."</string>
<string name="clone">Clone</string>
<string name="noLocationCouldBeFound">No position could be found after a timeout of %1$s seconds.</string>
<string name="pleaseGiveBgLocation">In the next screen please go to permissions, then location. There select \"Allow all the time\" to allow Automation to determine your location in the background.</string>
<string name="vibrate">Vibrate</string>
<string name="test">Try</string>
<string name="VibrateExplanation">Enter a vibration duration, followed by a comma, then a pause duration. You can concatenate as many vibrations as you like. Separate them by commas again. E.g. the pattern 100,500,500,1000,100 will vibrate 100, wait 500, vibrate 500, wait 1000, vibrate 100 ms. If you think a vibration is dropped, try increasing the pause before them.</string>
<string name="pleaseEnterValidVibrationPattern">Please enter a valid vibration pattern.</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="top">Top</string>
<string name="bottom">Bottom</string>
<string name="tabsPlacement">Position of tab bar</string>
<string name="tabsPlacementSummary">Choose where the tabs bar should be placed.</string>
</resources>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,7 @@
* SSID für WLANs kann nun aus Liste ausgewählt werden
* WLAN Router aktivieren benötigt keine root-Rechte mehr
* Telefonanruf-Auslöser erheblich verbessert
* Problem behoben, bei dem die Nutzung von Root-Rechten nicht funktioniert hat
* Absturz behoben, der in der F-Droid Version aufgetreten ist
* Spanische Übersetzung aktualisiert
* Italienische Übersetzung verbessert

View File

@ -0,0 +1,6 @@
* Übersetzungen aktualisiert.
* Layout Verbesserungen
* Fehler behoben, bei dem die Konfiguration auf manchen Geräten nicht exportiert werden konnte
* Ein Problem behoben, bei dem auf manchen Geräten keine Orte verwaltet werden konnten (die Position konnte nicht gefunden werden)
* Automatische Prüfung auf neue Versionen in der APK Version
* Regeln können jetzt geklont werden.

View File

@ -0,0 +1,4 @@
* Übersetzungen aktualisiert.
* Neue Aktion: Vibrieren
* Geschwindigkeitsberechnung verbessert
* Position der Tableiste kann gewählt werden (oben/unten)

View File

@ -39,51 +39,13 @@ Mögliche Aktionen:
* SMS verschicken
* Sounddatei abspielen.
Es ist ziemlich schwierig diese Anwendung über die vielen verschiedenen Geräte sowie die vielen Änderungen an Android Versionen am Funktionen zu halten. Ich kann vieles im Emulator testen, aber eben nicht alles.
Wenn also eine bestimmte Funktion nicht so tut wie sie sollte - lassen Sie es mich wissen. Über die Jahre habe ich noch alle Fehler behoben, die mir gemeldet wurden. Aber dafür bin ich auf Ihre Mithilfe angewiesen.
Es ist ziemlich schwierig diese Anwendung über die vielen verschiedenen Geräte sowie die vielen Änderungen an Android Versionen am Laufen zu halten. Ich kann vieles im Emulator testen, aber eben nicht alles.
Wenn also eine bestimmte Funktion nicht so tut wie sie sollte - lassen Sie es mich wissen. Über die Jahre habe ich noch alle Fehler behoben, die mir vernünftig gemeldet wurden. Aber dafür bin ich auf Ihre Mithilfe angewiesen.
Ein Wort zu den vielen Berechtigungen....
Wenn Sie ein Problem mit der Anwendung haben und mich dazu kontaktieren möchten, updaten Sie bitte vorher auf die neueste Version und schauen Sie, ob Ihr Problem darin auch besteht.
Es liegt in der Natur einer Anwendung wie dieser, daß sie ziemlich viele Berechtigungen benötigt. Allerdings sind praktisch alle davon optional, und werden nur angefragt, wenn Sie eine Regel erstellen, die das erfordert.
Spenden sind sicher eine gute, aber nicht die einzige Möglichkeit mich zu motivieren :-)
* Wer mir etwas Gutes tun will, kann die Anwendung auch im Play Store bewerten.
* Außerdem ist Hilfe bei der Übersetzung willkommen. Englisch, Spanisch und Deutsch kann ich selbst. Aber sonst ist alles gern gesehen.
Gehen wir sie mal schnell durch:
ACCESS_NETWORK_STATE, CHANGE_NETWORK_STATE: Dinge wie Flugmodus oder Roaming prüfen.
ACCESS_WIFI_STATE, CHANGE_WIFI_STATE: WLAN ein-/ausschalten
INTERNET
Das wird aus drei Gründen benötigt:
- Sie verwenden eine Ortungsmethode, die Mobilfunktürme verwenden (Standardeinstellung)
- Sie verwenden URL aufrufen als Aktion
- Sie aktivieren den Newsdownload in den Einstellungen
BLUETOOTH, BLUETOOTH_ADMIN: Bluetooth Verbindungen prüfen oder Bluetooth ein-/ausschalten
NFC: NFC Tags als Auslöser verwenden
Location (ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, ACCESS_BACKGROUND_LOCATION): Das sollte selbsterklärend sein - verwenden Sie Orte oder Geschwindigkeit als Auslöser?
PROCESS_OUTGOING_CALLS: Sie können ausgehende Anrufen als Auslöser verwenden. Z.B. "wenn die Frau anruft, dann..."
SEND_SMS: Sie können SMS verschicken lassen. Hier kann eine Zielnummer direkt eingegeben werden oder optional eine aus den Kontakten ausgewählt werden, was uns zu READ_CONTACTS bringt
READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE: Wenn Sie die Anwendung vor März 2021 installiert haben, war das nötig, um die Konfigurationsdatei zu speichern (unter sdcard). Seitdem ist sie nicht mehr notwendig. Die Berechtigung ist noch da, um bei Benutzern, die noch nicht geupdatet haben, ein Migrieren der Dateien zu ermöglichen. Weiterhin ist das Lesen jetzt notwendig, um eine Sounddatei abzuspielen.
GET_TASKS: Für den Auslöser, der prüft, ob eine andere Anwendung läuft.
BATTERY_STATS: Akkustand lesen.
MODIFY_AUDIO_SETTINGS, ACCESS_NOTIFICATION_POLICY:
Für höhere Android Versionen ist das nötig, um Toneinstellungen wie Klingeltöne zu ändern.
https://stackoverflow.com/questions/43123650/android-request-access-notification-policy-and-mute-phone/43127589#43127589
ACCESS_NOTIFICATION_POLICY wird auch verwendet, um für den entsprechenden Auslöser Benachrichtigungen anderer Anwendungen lesen zu können.
RECORD_AUDIO: Für den Auslöser "Hintergrundlautstärke" notwendig. Nebenbei - meine persönliche Anwendung dafür ist: Mein Telefon stellt sich morgens auf laut. Während der Woche ist das recht fr<66>h. Was ist aber, wenn ich einen Tag frei habe? Hierzu wird als zusätzliche Bedingung die Hintergrundlautstärke ausgewertet, um festzustellen, ob ich wirklich schon wach bin. Leider hat Google diese Funktion mit Android 8 deaktiviert.
READ_PHONE_STATE: https://developer.android.com/reference/android/Manifest.permission#READ_PHONE_STATE
RECEIVE_BOOT_COMPLETED, FOREGROUND_SERVICE, WAKE_LOCK: Den Dienst automatisch beim Systemstart starten.
WRITE_SETTINGS: Systemeinstellungen ändern.
ACCESS_SUPERUSER: root ist komplett optional. In der (weiter zurückliegenden) Vergangenheit war es möglich Dinge wie das Ein-/Ausschalten des USB Routers mit normalen Funktionen zu steuern. Leider ist das schon lange nur noch mit Root-Rechten möglich. D.h. root ist nur für wenige Funktionen notwendig.
Erklärungen zu den vielen Berechtigungen können hier abgerufen werden: https://server47.de/automation/permissions_de.html

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