133 Commits

Author SHA1 Message Date
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
e0c3b9d450 Changelog updated and translated to German. 2021-04-27 15:51:02 +02:00
c134b15ab1 Changelog updated and translated to German. 2021-04-27 15:43:52 +02:00
6ef6277976 Changelog updated and translated to German. 2021-04-27 15:30:46 +02:00
10e6c74ba8 Cleanups. 2021-04-27 14:49:34 +02:00
b6f3d928ae Cleanups. 2021-04-27 14:42:29 +02:00
e898264178 StartAppChanges 2021-04-20 19:49:19 +02:00
0891dca98d StartAppChanges 2021-04-17 14:52:58 +02:00
8843a97b79 StartAppChanges 2021-04-13 20:00:36 +02:00
1c24af7bcb StartAppChanges 2021-04-11 19:37:50 +02:00
ee43e109da StartAppChanges 2021-04-09 17:39:59 +02:00
13bcb02ffc Notification trigger. 2021-03-30 23:06:07 +02:00
864ed2111e Notification listener finished. 2021-03-30 19:52:31 +02:00
67e6a38ddc Notification listener finished. 2021-03-29 16:36:21 +02:00
fb44196a0d Notification trigger. 2021-03-28 23:23:29 +02:00
7894504791 Notification listener started. 2021-03-28 20:33:44 +02:00
0df5342036 Notification trigger. 2021-03-27 22:52:42 +01:00
712a374adb Notification listener started. 2021-03-27 19:57:39 +01:00
80f8f9cfe2 Notification listener started. 2021-03-27 14:11:39 +01:00
1d9ed8b3ff Notification trigger. 2021-03-26 23:11:36 +01:00
5d6221888a Notification listener started. 2021-03-26 19:58:27 +01:00
56806f0349 Merge remote-tracking branch 'origin/master' into development
# Conflicts:
#	app/googlePlayFlavor/release/output-metadata.json
2021-03-21 22:45:34 +01:00
7127ac45bc Fixed app crashing when saving rules while service is running. 2021-03-21 22:44:40 +01:00
8a43741b21 Merge remote-tracking branch 'origin/master' into development 2021-03-21 22:30:20 +01:00
b7ebf2cdcd Fixed app crashing when saving rules while service is running. 2021-03-21 22:27:34 +01:00
054ab6d84b Background location notifications in googlePlayFlavor 2021-03-20 22:49:05 +01:00
47b56d4978 Google shit again. 2021-03-20 13:37:18 +01:00
d4288dfc40 Google shit again. 2021-03-20 12:18:37 +01:00
01f4bba995 Google shit again. 2021-03-20 12:16:39 +01:00
4a2a1a0550 Google shit again. 2021-03-20 02:44:27 +01:00
a693ced32e Background location notifications in googlePlayFlavor 2021-03-18 23:37:21 +01:00
64d1aec910 Google shit again. 2021-03-18 20:00:19 +01:00
221cfa4339 Trying to fix notifications. 2021-03-17 22:58:58 +01:00
810b6f03aa Google shit again. 2021-03-17 21:42:01 +01:00
4e51cbe36e GooglePlayFlavor location engine disabled notice. 2021-03-16 23:23:42 +01:00
722f9f0e6b News opt-in done. 2021-03-15 23:06:37 +01:00
c1f03d3395 Update 'fastlane/metadata/android/en-US/full_description.txt' 2021-03-14 14:25:35 +01:00
fc71a3548c Migration of old files finished. 2021-03-13 00:54:17 +01:00
e33915b578 Migration of old files finished. 2021-03-13 00:40:59 +01:00
bdfded960c Migration of old files unfinished. 2021-03-12 22:38:52 +01:00
d803d57af1 Changed settings location to regular data folder. Migration of files in existing installations pending. 2021-03-07 23:45:47 +01:00
f9cf3356e7 Merge branches 'development' and 'master' of https://git.server47.de/jens/Automation into development 2021-03-07 12:08:29 +01:00
81be087252 Google shit again. 2021-03-06 02:04:01 +01:00
99d630baae Merge branches 'development' and 'master' of https://git.server47.de/jens/Automation into development 2021-03-06 00:25:53 +01:00
d64f6e2c3b News download finished. 2021-03-02 19:55:43 +01:00
8a5fa20cad Started SDK 29 background location crap. 2021-02-28 23:06:20 +01:00
5758e5ee2b News done. 2021-02-25 22:50:36 +01:00
149 changed files with 12488 additions and 4673 deletions

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

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

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

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

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

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

View File

@ -11,8 +11,8 @@ android {
compileSdkVersion 29 compileSdkVersion 29
buildToolsVersion '29.0.2' buildToolsVersion '29.0.2'
useLibrary 'org.apache.http.legacy' useLibrary 'org.apache.http.legacy'
versionCode 96 versionCode 107
versionName "1.6.21" versionName "1.6.36"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@ -40,7 +40,6 @@ android {
googlePlayFlavor googlePlayFlavor
{ {
dimension "version" dimension "version"
// applicationIdSuffix ".googlePlay"
versionNameSuffix "-googlePlay" versionNameSuffix "-googlePlay"
targetSdkVersion 29 targetSdkVersion 29
} }
@ -48,15 +47,12 @@ android {
fdroidFlavor fdroidFlavor
{ {
dimension "version" dimension "version"
// applicationIdSuffix ".fdroid"
// versionNameSuffix "-fdroid"
targetSdkVersion 28 targetSdkVersion 28
} }
apkFlavor apkFlavor
{ {
dimension "version" dimension "version"
// applicationIdSuffix ".apk"
versionNameSuffix "-apk" versionNameSuffix "-apk"
targetSdkVersion 28 targetSdkVersion 28
} }
@ -64,13 +60,18 @@ android {
} }
dependencies { dependencies {
googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0' googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0' googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0'
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0' apkFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:17.1.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' implementation 'com.google.android.material:material:1.3.0'
testImplementation 'junit:junit:4.+' testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'

View File

@ -0,0 +1,18 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.jens.automation2",
"variantName": "googlePlayFlavorRelease",
"elements": [
{
"type": "SINGLE",
"filters": [],
"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.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" /> <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.GET_TASKS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />
@ -62,8 +63,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <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 <uses-feature
android:name="android.hardware.telephony" android:name="android.hardware.telephony"
@ -76,7 +77,7 @@
android:allowBackup="true" android:allowBackup="true"
android:allowClearUserData="true" android:allowClearUserData="true"
android:icon="@drawable/gears" android:icon="@drawable/gears"
android:label="@string/title_activity_main" android:label="@string/app_name"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config"> android:networkSecurityConfig="@xml/network_security_config">
@ -95,15 +96,15 @@
android:label="@string/app_name"></activity> android:label="@string/app_name"></activity>
<activity <activity
android:name=".ActivityManagePoi" android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity> android:label="@string/app_name"></activity>
<activity <activity
android:name=".ActivitySettings" android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity> android:label="@string/app_name"></activity>
<service <service
android:name=".AutomationService" android:name=".AutomationService"
android:exported="false" 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"> <receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter> <intent-filter>
@ -136,11 +137,16 @@
<receiver android:name=".receivers.TimeZoneListener" /> <receiver android:name=".receivers.TimeZoneListener" />
<activity android:name=".ActivityManageRule" /> <activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" /> <activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" /> <activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageTimeFrame" /> <activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageBrightnessSetting" /> <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=".ActivityHelp" />
<activity android:name=".ActivityManageActionVibrate" />
<activity <activity
android:name=".ActivityMainTabLayout" android:name=".ActivityMainTabLayout"
android:launchMode="singleTask"> android:launchMode="singleTask">
@ -177,13 +183,31 @@
<activity android:name=".ActivityMainPoi" /> <activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" /> <activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" /> <activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" /> <activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageNfc" /> <activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityEditSpeakText" /> <activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" /> <activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" /> <activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" /> <activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" /> <activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<!-- 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 <service
android:name=".receivers.ActivityDetectionReceiver" android:name=".receivers.ActivityDetectionReceiver"
@ -193,15 +217,15 @@
<meta-data <meta-data
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_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"/> <service android:name=".location.GeofenceIntentService"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application> </application>
</manifest> </manifest>

View File

@ -1,13 +1,18 @@
package com.jens.automation2; package com.jens.automation2;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Looper; import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.gms.location.DetectedActivity; import com.google.android.gms.location.DetectedActivity;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.ActivityDetectionReceiver; import com.jens.automation2.receivers.ActivityDetectionReceiver;
import com.jens.automation2.receivers.BatteryReceiver; import com.jens.automation2.receivers.BatteryReceiver;
@ -16,6 +21,7 @@ import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.HeadphoneJackListener; import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NfcReceiver; import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener; import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener; import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener; import com.jens.automation2.receivers.ProcessListener;
@ -24,6 +30,10 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import static com.jens.automation2.Trigger.triggerParameter2Split;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE;
public class Rule implements Comparable<Rule> public class Rule implements Comparable<Rule>
{ {
@ -42,9 +52,20 @@ public class Rule implements Comparable<Rule>
private String name; private String name;
private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily
private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable
private Calendar lastExecution;
private static Date lastActivatedRuleActivationTime; private static Date lastActivatedRuleActivationTime;
public Calendar getLastExecution()
{
return lastExecution;
}
public void setLastExecution(Calendar lastExecution)
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle() public boolean isRuleToggle()
{ {
return ruleToggle; return ruleToggle;
@ -114,6 +135,7 @@ public class Rule implements Comparable<Rule>
{ {
return this.getName(); return this.getName();
} }
@SuppressLint("NewApi")
public String toStringLong() public String toStringLong()
{ {
String returnString = ""; String returnString = "";
@ -153,6 +175,15 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3); Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3);
ruleCollection.add(this); ruleCollection.add(this);
boolean returnValue = XmlFileInterface.writeFile(); boolean returnValue = XmlFileInterface.writeFile();
try
{
XmlFileInterface.readFile();
}
catch(Exception e)
{
Miscellaneous.logEvent("w", "Read file", Log.getStackTraceString(e), 3);
}
if(returnValue) if(returnValue)
{ {
@ -196,10 +227,23 @@ public class Rule implements Comparable<Rule>
return XmlFileInterface.writeFile(); 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) 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(); Toast.makeText(context, context.getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false; return false;
@ -394,13 +438,13 @@ public class Rule implements Comparable<Rule>
&& &&
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0 Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0
) )
| ||
// Other case, start time higher than end time, timeframe goes over midnight // 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(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0
&& &&
(Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 (Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0
| ||
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0) Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0)
) )
@ -469,7 +513,7 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerParameter()) if(oneTrigger.getTriggerParameter())
{ {
if(BatteryReceiver.getBatteryLevel() < oneTrigger.getBatteryLevel()) if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false; return false;
@ -477,7 +521,7 @@ public class Rule implements Comparable<Rule>
} }
else else
{ {
if(BatteryReceiver.getBatteryLevel() > oneTrigger.getBatteryLevel()) if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false; return false;
@ -488,7 +532,7 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerParameter()) if(oneTrigger.getTriggerParameter())
{ {
if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed()) if(LocationProvider.getSpeed() < oneTrigger.getSpeed())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false; return false;
@ -496,7 +540,7 @@ public class Rule implements Comparable<Rule>
} }
else else
{ {
if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed()) if(LocationProvider.getSpeed() > oneTrigger.getSpeed())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false; return false;
@ -527,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); 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.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); 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; return false;
} }
else else
@ -580,13 +624,29 @@ public class Rule implements Comparable<Rule>
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall)) 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 else
{ {
@ -601,7 +661,10 @@ public class Rule implements Comparable<Rule>
} }
} }
else else
{
Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); 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)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag))
{ {
@ -648,109 +711,69 @@ public class Rule implements Comparable<Rule>
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection))
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4);
// if( // connected / disconnected if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED")))
// |
// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED")))
// )
// {
// if(oneTrigger.getBluetoothDeviceAddress() != null)
// {
// if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4);
// }
// else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
// {
// // ???
// }
// else
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4);
// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress()))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3);
// return false;
// }
// else
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4);
// }
// }
// }
// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND))
// {
// if(!oneTrigger.getTriggerParameter())
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3);
// return false;
// }
// }
// else // above only checks for last action, this checks for things in the past
{ {
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>")) if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{ {
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
{ return false;
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
} }
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{ {
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
{ return false;
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
} }
else else
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); // range
return false; if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
} }
} }
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
}
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged))
{ {
@ -763,6 +786,109 @@ public class Rule implements Comparable<Rule>
return false; return false;
} }
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
String myApp = params[0];
String myTitleDir = params[1];
String myTitle = params[2];
String myTextDir = params[3];
String myText;
if (params.length >= 5)
myText = params[4];
else
myText = "";
if(oneTrigger.getTriggerParameter())
{
// Check an active notification that is still there
boolean foundMatch = false;
for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
{
if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis())
{
String app = sbn.getPackageName();
String title = sbn.getNotification().extras.getString(EXTRA_TITLE);
String text = sbn.getNotification().extras.getString(EXTRA_TEXT);
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, myTitle, title))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, myText, text))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
foundMatch = true;
break;
}
}
if(!foundMatch)
return false;
}
else
{
// check a notification that is gone
if(NotificationListener.getLastNotification() != null)
{
if(!NotificationListener.getLastNotification().isCreated())
{
String app = NotificationListener.getLastNotification().getApp();
String title = NotificationListener.getLastNotification().getTitle();
String text = NotificationListener.getLastNotification().getText();
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
return false;
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, title, myTitle))
return false;
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, text, myText))
return false;
}
}
else
return false;
}
}
}
}
} }
return true; return true;
@ -771,9 +897,11 @@ 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); 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; return false;
} }
private class ActivateRuleTask extends AsyncTask<Object, String, Void> private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{ {
boolean wasActivated = false;
@Override @Override
protected Void doInBackground(Object... params) protected Void doInBackground(Object... params)
{ {
@ -788,7 +916,7 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null) if (Looper.myLooper() == null)
Looper.prepare(); Looper.prepare();
activateInternally((AutomationService)params[0], (Boolean)params[1]); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null; return null;
} }
@ -806,27 +934,35 @@ public class Rule implements Comparable<Rule>
@Override @Override
protected void onPostExecute(Void result) protected void onPostExecute(Void result)
{ {
AutomationService.updateNotification(); /*
ActivityMainScreen.updateMainScreen(); Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule
super.onPostExecute(result); with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop.
} */
if(wasActivated)
{
setLastExecution(Calendar.getInstance());
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
/** /**
* Will activate the rule. Should be called by a separate execution thread * Will activate the rule. Should be called by a separate execution thread
* @param automationService * @param automationService
*/ */
protected void activateInternally(AutomationService automationService, boolean force) protected boolean activateInternally(AutomationService automationService, boolean force)
{ {
boolean isActuallyToggable = isActuallyToggable(); boolean isActuallyToggable = isActuallyToggable();
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable; boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle) if(notLastActive || force || doToggle)
{ {
String message; String message;
if(!doToggle) 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 else
message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName()); message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName());
Miscellaneous.logEvent("i", "Rule", message, 2); Miscellaneous.logEvent("i", "Rule", message, 2);
@ -834,10 +970,19 @@ public class Rule implements Comparable<Rule>
// Toast.makeText(automationService, message, Toast.LENGTH_LONG).show(); // Toast.makeText(automationService, message, Toast.LENGTH_LONG).show();
if(Settings.startNewThreadForRuleActivation) if(Settings.startNewThreadForRuleActivation)
publishProgress(message); publishProgress(message);
for(int i = 0; i< Rule.this.getActionSet().size(); i++) for(int i = 0; i< Rule.this.getActionSet().size(); i++)
Rule.this.getActionSet().get(i).run(automationService, doToggle); {
try
{
Rule.this.getActionSet().get(i).run(automationService, doToggle);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1);
}
}
// Keep log of last x rule activations (Settings) // Keep log of last x rule activations (Settings)
try try
{ {
@ -862,9 +1007,12 @@ public class Rule implements Comparable<Rule>
else else
{ {
Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3);
return false;
} }
}
} return true;
}
}
public void activate(AutomationService automationService, boolean force) public void activate(AutomationService automationService, boolean force)
{ {
@ -963,7 +1111,7 @@ public class Rule implements Comparable<Rule>
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()) if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime())
{ {
Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5); 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); ruleCandidates.add(oneRule);
break innerloop; //if the poi is found we don't need to search the other triggers in the same rule break innerloop; //if the poi is found we don't need to search the other triggers in the same rule
@ -1234,10 +1382,10 @@ public class Rule implements Comparable<Rule>
return ruleCandidates; return ruleCandidates;
} }
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(boolean triggerParameter) public static ArrayList<Rule> findRuleCandidatesByPhoneCall(String direction)
{ {
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>(); ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection) for(Rule oneRule : ruleCollection)
{ {
innerloop: innerloop:
@ -1245,7 +1393,8 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.phoneCall) 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); ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule break innerloop; //we don't need to search the other triggers in the same rule
@ -1253,7 +1402,7 @@ public class Rule implements Comparable<Rule>
} }
} }
} }
return ruleCandidates; return ruleCandidates;
} }
@ -1279,6 +1428,26 @@ public class Rule implements Comparable<Rule>
return ruleCandidates; return ruleCandidates;
} }
public static ArrayList<Rule> findRuleCandidates(Trigger.Trigger_Enum triggerType)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == triggerType)
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
}
}
}
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByActivityDetection() public static ArrayList<Rule> findRuleCandidatesByActivityDetection()
{ {

View File

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

View File

@ -1,12 +1,17 @@
package com.jens.automation2; package com.jens.automation2;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Looper; import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.BatteryReceiver; import com.jens.automation2.receivers.BatteryReceiver;
import com.jens.automation2.receivers.BluetoothReceiver; import com.jens.automation2.receivers.BluetoothReceiver;
@ -14,6 +19,7 @@ import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.HeadphoneJackListener; import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NfcReceiver; import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener; import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener; import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener; import com.jens.automation2.receivers.ProcessListener;
@ -22,6 +28,9 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import static com.jens.automation2.Trigger.triggerParameter2Split;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE;
public class Rule implements Comparable<Rule> public class Rule implements Comparable<Rule>
{ {
@ -40,8 +49,19 @@ public class Rule implements Comparable<Rule>
private String name; private String name;
private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily
private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable
private Calendar lastExecution;
private static Date lastActivatedRuleActivationTime; private static Date lastActivatedRuleActivationTime;
public Calendar getLastExecution()
{
return lastExecution;
}
public void setLastExecution(Calendar lastExecution)
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle() public boolean isRuleToggle()
{ {
@ -112,6 +132,7 @@ public class Rule implements Comparable<Rule>
{ {
return this.getName(); return this.getName();
} }
@SuppressLint("NewApi")
public String toStringLong() public String toStringLong()
{ {
String returnString = ""; String returnString = "";
@ -151,6 +172,15 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3); Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3);
ruleCollection.add(this); ruleCollection.add(this);
boolean returnValue = XmlFileInterface.writeFile(); boolean returnValue = XmlFileInterface.writeFile();
try
{
XmlFileInterface.readFile();
}
catch(Exception e)
{
Miscellaneous.logEvent("w", "Read file", Log.getStackTraceString(e), 3);
}
if(returnValue) if(returnValue)
{ {
@ -197,7 +227,7 @@ public class Rule implements Comparable<Rule>
private boolean checkBeforeSaving(Context context, boolean changeExistingRule) 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(); Toast.makeText(context, context.getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false; return false;
@ -392,13 +422,13 @@ public class Rule implements Comparable<Rule>
&& &&
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0 Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0
) )
| ||
// Other case, start time higher than end time, timeframe goes over midnight // 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(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0
&& &&
(Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 (Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0
| ||
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0) Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0)
) )
@ -467,7 +497,7 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerParameter()) if(oneTrigger.getTriggerParameter())
{ {
if(BatteryReceiver.getBatteryLevel() < oneTrigger.getBatteryLevel()) if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false; return false;
@ -475,7 +505,7 @@ public class Rule implements Comparable<Rule>
} }
else else
{ {
if(BatteryReceiver.getBatteryLevel() > oneTrigger.getBatteryLevel()) if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false; return false;
@ -486,7 +516,7 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerParameter()) if(oneTrigger.getTriggerParameter())
{ {
if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed()) if(LocationProvider.getSpeed() < oneTrigger.getSpeed())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false; return false;
@ -494,7 +524,7 @@ public class Rule implements Comparable<Rule>
} }
else else
{ {
if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed()) if(LocationProvider.getSpeed() > oneTrigger.getSpeed())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false; return false;
@ -525,12 +555,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); 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.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); 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; return false;
} }
else else
@ -578,13 +608,29 @@ public class Rule implements Comparable<Rule>
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall)) 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 else
{ {
@ -599,7 +645,10 @@ public class Rule implements Comparable<Rule>
} }
} }
else else
{
Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); 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)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag))
{ {
@ -617,109 +666,69 @@ public class Rule implements Comparable<Rule>
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection))
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4);
// if( // connected / disconnected if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED")))
// |
// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED")))
// )
// {
// if(oneTrigger.getBluetoothDeviceAddress() != null)
// {
// if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4);
// }
// else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
// {
// // ???
// }
// else
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4);
// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress()))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3);
// return false;
// }
// else
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4);
// }
// }
// }
// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND))
// {
// if(!oneTrigger.getTriggerParameter())
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3);
// return false;
// }
// }
// else // above only checks for last action, this checks for things in the past
{ {
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>")) if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{ {
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
{ return false;
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
} }
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{ {
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
{ return false;
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
} }
else else
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); // range
return false; if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
} }
} }
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
}
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged))
{ {
@ -732,6 +741,109 @@ public class Rule implements Comparable<Rule>
return false; return false;
} }
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
String myApp = params[0];
String myTitleDir = params[1];
String myTitle = params[2];
String myTextDir = params[3];
String myText;
if (params.length >= 5)
myText = params[4];
else
myText = "";
if(oneTrigger.getTriggerParameter())
{
// Check an active notification that is still there
boolean foundMatch = false;
for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
{
if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis())
{
String app = sbn.getPackageName();
String title = sbn.getNotification().extras.getString(EXTRA_TITLE);
String text = sbn.getNotification().extras.getString(EXTRA_TEXT);
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, myTitle, title))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, myText, text))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
foundMatch = true;
break;
}
}
if(!foundMatch)
return false;
}
else
{
// check a notification that is gone
if(NotificationListener.getLastNotification() != null)
{
if(!NotificationListener.getLastNotification().isCreated())
{
String app = NotificationListener.getLastNotification().getApp();
String title = NotificationListener.getLastNotification().getTitle();
String text = NotificationListener.getLastNotification().getText();
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
return false;
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, title, myTitle))
return false;
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, text, myText))
return false;
}
}
else
return false;
}
}
}
}
} }
return true; return true;
@ -743,6 +855,8 @@ public class Rule implements Comparable<Rule>
private class ActivateRuleTask extends AsyncTask<Object, String, Void> private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{ {
boolean wasActivated = false;
@Override @Override
protected Void doInBackground(Object... params) protected Void doInBackground(Object... params)
{ {
@ -757,7 +871,7 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null) if (Looper.myLooper() == null)
Looper.prepare(); Looper.prepare();
activateInternally((AutomationService)params[0], (Boolean)params[1]); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null; return null;
} }
@ -775,27 +889,35 @@ public class Rule implements Comparable<Rule>
@Override @Override
protected void onPostExecute(Void result) protected void onPostExecute(Void result)
{ {
AutomationService.updateNotification(); /*
ActivityMainScreen.updateMainScreen(); Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule
super.onPostExecute(result); with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop.
} */
if(wasActivated)
{
setLastExecution(Calendar.getInstance());
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
/** /**
* Will activate the rule. Should be called by a separate execution thread * Will activate the rule. Should be called by a separate execution thread
* @param automationService * @param automationService
*/ */
protected void activateInternally(AutomationService automationService, boolean force) protected boolean activateInternally(AutomationService automationService, boolean force)
{ {
boolean isActuallyToggable = isActuallyToggable(); boolean isActuallyToggable = isActuallyToggable();
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable; boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle) if(notLastActive || force || doToggle)
{ {
String message; String message;
if(!doToggle) 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 else
message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName()); message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName());
Miscellaneous.logEvent("i", "Rule", message, 2); Miscellaneous.logEvent("i", "Rule", message, 2);
@ -803,10 +925,19 @@ public class Rule implements Comparable<Rule>
// Toast.makeText(automationService, message, Toast.LENGTH_LONG).show(); // Toast.makeText(automationService, message, Toast.LENGTH_LONG).show();
if(Settings.startNewThreadForRuleActivation) if(Settings.startNewThreadForRuleActivation)
publishProgress(message); publishProgress(message);
for(int i = 0; i< Rule.this.getActionSet().size(); i++) for(int i = 0; i< Rule.this.getActionSet().size(); i++)
Rule.this.getActionSet().get(i).run(automationService, doToggle); {
try
{
Rule.this.getActionSet().get(i).run(automationService, doToggle);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1);
}
}
// Keep log of last x rule activations (Settings) // Keep log of last x rule activations (Settings)
try try
{ {
@ -831,9 +962,12 @@ public class Rule implements Comparable<Rule>
else else
{ {
Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3);
return false;
} }
}
} return true;
}
}
public void activate(AutomationService automationService, boolean force) public void activate(AutomationService automationService, boolean force)
{ {
@ -932,7 +1066,7 @@ public class Rule implements Comparable<Rule>
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()) if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime())
{ {
Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5); 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); ruleCandidates.add(oneRule);
break innerloop; //if the poi is found we don't need to search the other triggers in the same rule break innerloop; //if the poi is found we don't need to search the other triggers in the same rule
@ -1203,10 +1337,10 @@ public class Rule implements Comparable<Rule>
return ruleCandidates; return ruleCandidates;
} }
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(boolean triggerParameter) public static ArrayList<Rule> findRuleCandidatesByPhoneCall(String direction)
{ {
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>(); ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection) for(Rule oneRule : ruleCollection)
{ {
innerloop: innerloop:
@ -1214,7 +1348,8 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.phoneCall) 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); ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule break innerloop; //we don't need to search the other triggers in the same rule
@ -1222,7 +1357,7 @@ public class Rule implements Comparable<Rule>
} }
} }
} }
return ruleCandidates; return ruleCandidates;
} }
@ -1248,6 +1383,26 @@ public class Rule implements Comparable<Rule>
return ruleCandidates; return ruleCandidates;
} }
public static ArrayList<Rule> findRuleCandidates(Trigger.Trigger_Enum triggerType)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == triggerType)
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
}
}
}
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByActivityDetection() public static ArrayList<Rule> findRuleCandidatesByActivityDetection()
{ {

View File

@ -36,7 +36,6 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -62,21 +61,14 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <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"
android:required="false" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS"/>
-->
<application <application
android:allowBackup="true" android:allowBackup="true"
android:allowClearUserData="true" android:allowClearUserData="true"
android:icon="@drawable/gears" android:icon="@drawable/gears"
android:label="@string/title_activity_main" android:label="@string/app_name"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config"> android:networkSecurityConfig="@xml/network_security_config">
@ -95,15 +87,15 @@
android:label="@string/app_name"></activity> android:label="@string/app_name"></activity>
<activity <activity
android:name=".ActivityManagePoi" android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity> android:label="@string/app_name"></activity>
<activity <activity
android:name=".ActivitySettings" android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity> android:label="@string/app_name"></activity>
<service <service
android:name=".AutomationService" android:name=".AutomationService"
android:exported="false" 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"> <receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter> <intent-filter>
@ -136,11 +128,16 @@
<receiver android:name=".receivers.TimeZoneListener" /> <receiver android:name=".receivers.TimeZoneListener" />
<activity android:name=".ActivityManageRule" /> <activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" /> <activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" /> <activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageTimeFrame" /> <activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageBrightnessSetting" /> <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=".ActivityHelp" />
<activity android:name=".ActivityManageActionVibrate" />
<activity <activity
android:name=".ActivityMainTabLayout" android:name=".ActivityMainTabLayout"
android:launchMode="singleTask"> android:launchMode="singleTask">
@ -177,13 +174,34 @@
<activity android:name=".ActivityMainPoi" /> <activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" /> <activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" /> <activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" /> <activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageNfc" /> <activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityEditSpeakText" /> <activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" /> <activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" /> <activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" /> <activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" /> <activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity android:name=".ActivityPermissions" />
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<service <service
android:name=".receivers.ActivityDetectionReceiver" android:name=".receivers.ActivityDetectionReceiver"
@ -193,12 +211,14 @@
<meta-data <meta-data
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" /> android:value="@integer/google_play_services_version" />
<activity android:name=".ActivityPermissions"></activity>
<service android:name=".location.GeofenceIntentService"/> <service android:name=".location.GeofenceIntentService"/>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application> </application>

View File

@ -1,13 +1,18 @@
package com.jens.automation2; package com.jens.automation2;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Looper; import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.gms.location.DetectedActivity; import com.google.android.gms.location.DetectedActivity;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.ActivityDetectionReceiver; import com.jens.automation2.receivers.ActivityDetectionReceiver;
import com.jens.automation2.receivers.BatteryReceiver; import com.jens.automation2.receivers.BatteryReceiver;
@ -16,6 +21,7 @@ import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.HeadphoneJackListener; import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NfcReceiver; import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener; import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener; import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener; import com.jens.automation2.receivers.ProcessListener;
@ -24,6 +30,9 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import static com.jens.automation2.Trigger.triggerParameter2Split;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE;
public class Rule implements Comparable<Rule> public class Rule implements Comparable<Rule>
{ {
@ -42,9 +51,20 @@ public class Rule implements Comparable<Rule>
private String name; private String name;
private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily
private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable
private Calendar lastExecution;
private static Date lastActivatedRuleActivationTime; private static Date lastActivatedRuleActivationTime;
public Calendar getLastExecution()
{
return lastExecution;
}
public void setLastExecution(Calendar lastExecution)
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle() public boolean isRuleToggle()
{ {
return ruleToggle; return ruleToggle;
@ -114,6 +134,7 @@ public class Rule implements Comparable<Rule>
{ {
return this.getName(); return this.getName();
} }
@SuppressLint("NewApi")
public String toStringLong() public String toStringLong()
{ {
String returnString = ""; String returnString = "";
@ -153,6 +174,15 @@ public class Rule implements Comparable<Rule>
Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3); Miscellaneous.logEvent("i", "Rule", "Creating rule: " + this.toString(), 3);
ruleCollection.add(this); ruleCollection.add(this);
boolean returnValue = XmlFileInterface.writeFile(); boolean returnValue = XmlFileInterface.writeFile();
try
{
XmlFileInterface.readFile();
}
catch(Exception e)
{
Miscellaneous.logEvent("w", "Read file", Log.getStackTraceString(e), 3);
}
if(returnValue) if(returnValue)
{ {
@ -196,10 +226,23 @@ public class Rule implements Comparable<Rule>
return XmlFileInterface.writeFile(); 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) 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(); Toast.makeText(context, context.getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show();
return false; return false;
@ -394,13 +437,13 @@ public class Rule implements Comparable<Rule>
&& &&
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0 Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0
) )
| ||
// Other case, start time higher than end time, timeframe goes over midnight // 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(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0
&& &&
(Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 (Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0
| ||
Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0) Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0)
) )
@ -469,7 +512,7 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerParameter()) if(oneTrigger.getTriggerParameter())
{ {
if(BatteryReceiver.getBatteryLevel() < oneTrigger.getBatteryLevel()) if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false; return false;
@ -477,7 +520,7 @@ public class Rule implements Comparable<Rule>
} }
else else
{ {
if(BatteryReceiver.getBatteryLevel() > oneTrigger.getBatteryLevel()) if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false; return false;
@ -488,7 +531,7 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerParameter()) if(oneTrigger.getTriggerParameter())
{ {
if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed()) if(LocationProvider.getSpeed() < oneTrigger.getSpeed())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false; return false;
@ -496,7 +539,7 @@ public class Rule implements Comparable<Rule>
} }
else else
{ {
if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed()) if(LocationProvider.getSpeed() > oneTrigger.getSpeed())
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false; return false;
@ -527,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); 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.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); 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; return false;
} }
else else
@ -580,13 +623,29 @@ public class Rule implements Comparable<Rule>
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall)) 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 else
{ {
@ -601,7 +660,10 @@ public class Rule implements Comparable<Rule>
} }
} }
else else
{
Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); 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)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag))
{ {
@ -648,109 +710,69 @@ public class Rule implements Comparable<Rule>
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection))
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4);
// if( // connected / disconnected if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED")))
// |
// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED")))
// )
// {
// if(oneTrigger.getBluetoothDeviceAddress() != null)
// {
// if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4);
// }
// else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
// {
// // ???
// }
// else
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4);
// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress()))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3);
// return false;
// }
// else
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4);
// }
// }
// }
// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND))
// {
// if(!oneTrigger.getTriggerParameter())
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3);
// return false;
// }
// }
// else // above only checks for last action, this checks for things in the past
{ {
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>")) if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{ {
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
{ return false;
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
} }
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{ {
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
{ return false;
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
} }
else else
{ {
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); // range
return false; if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
} }
} }
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
}
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged))
{ {
@ -763,6 +785,109 @@ public class Rule implements Comparable<Rule>
return false; return false;
} }
} }
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
String myApp = params[0];
String myTitleDir = params[1];
String myTitle = params[2];
String myTextDir = params[3];
String myText;
if (params.length >= 5)
myText = params[4];
else
myText = "";
if(oneTrigger.getTriggerParameter())
{
// Check an active notification that is still there
boolean foundMatch = false;
for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
{
if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis())
{
String app = sbn.getPackageName();
String title = sbn.getNotification().extras.getString(EXTRA_TITLE);
String text = sbn.getNotification().extras.getString(EXTRA_TEXT);
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, myTitle, title))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, myText, text))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
foundMatch = true;
break;
}
}
if(!foundMatch)
return false;
}
else
{
// check a notification that is gone
if(NotificationListener.getLastNotification() != null)
{
if(!NotificationListener.getLastNotification().isCreated())
{
String app = NotificationListener.getLastNotification().getApp();
String title = NotificationListener.getLastNotification().getTitle();
String text = NotificationListener.getLastNotification().getText();
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
return false;
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, title, myTitle))
return false;
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, text, myText))
return false;
}
}
else
return false;
}
}
}
}
} }
return true; return true;
@ -774,6 +899,8 @@ public class Rule implements Comparable<Rule>
private class ActivateRuleTask extends AsyncTask<Object, String, Void> private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{ {
boolean wasActivated = false;
@Override @Override
protected Void doInBackground(Object... params) protected Void doInBackground(Object... params)
{ {
@ -788,7 +915,7 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null) if (Looper.myLooper() == null)
Looper.prepare(); Looper.prepare();
activateInternally((AutomationService)params[0], (Boolean)params[1]); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null; return null;
} }
@ -806,27 +933,35 @@ public class Rule implements Comparable<Rule>
@Override @Override
protected void onPostExecute(Void result) protected void onPostExecute(Void result)
{ {
AutomationService.updateNotification(); /*
ActivityMainScreen.updateMainScreen(); Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule
super.onPostExecute(result); with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop.
} */
if(wasActivated)
{
setLastExecution(Calendar.getInstance());
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
/** /**
* Will activate the rule. Should be called by a separate execution thread * Will activate the rule. Should be called by a separate execution thread
* @param automationService * @param automationService
*/ */
protected void activateInternally(AutomationService automationService, boolean force) protected boolean activateInternally(AutomationService automationService, boolean force)
{ {
boolean isActuallyToggable = isActuallyToggable(); boolean isActuallyToggable = isActuallyToggable();
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable; boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle) if(notLastActive || force || doToggle)
{ {
String message; String message;
if(!doToggle) 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 else
message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName()); message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName());
Miscellaneous.logEvent("i", "Rule", message, 2); Miscellaneous.logEvent("i", "Rule", message, 2);
@ -834,10 +969,19 @@ public class Rule implements Comparable<Rule>
// Toast.makeText(automationService, message, Toast.LENGTH_LONG).show(); // Toast.makeText(automationService, message, Toast.LENGTH_LONG).show();
if(Settings.startNewThreadForRuleActivation) if(Settings.startNewThreadForRuleActivation)
publishProgress(message); publishProgress(message);
for(int i = 0; i< Rule.this.getActionSet().size(); i++) for(int i = 0; i< Rule.this.getActionSet().size(); i++)
Rule.this.getActionSet().get(i).run(automationService, doToggle); {
try
{
Rule.this.getActionSet().get(i).run(automationService, doToggle);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1);
}
}
// Keep log of last x rule activations (Settings) // Keep log of last x rule activations (Settings)
try try
{ {
@ -862,9 +1006,12 @@ public class Rule implements Comparable<Rule>
else else
{ {
Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3);
return false;
} }
}
} return true;
}
}
public void activate(AutomationService automationService, boolean force) public void activate(AutomationService automationService, boolean force)
{ {
@ -963,7 +1110,7 @@ public class Rule implements Comparable<Rule>
if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()) if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime())
{ {
Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5); 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); ruleCandidates.add(oneRule);
break innerloop; //if the poi is found we don't need to search the other triggers in the same rule break innerloop; //if the poi is found we don't need to search the other triggers in the same rule
@ -1234,10 +1381,10 @@ public class Rule implements Comparable<Rule>
return ruleCandidates; return ruleCandidates;
} }
public static ArrayList<Rule> findRuleCandidatesByPhoneCall(boolean triggerParameter) public static ArrayList<Rule> findRuleCandidatesByPhoneCall(String direction)
{ {
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>(); ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection) for(Rule oneRule : ruleCollection)
{ {
innerloop: innerloop:
@ -1245,7 +1392,8 @@ public class Rule implements Comparable<Rule>
{ {
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.phoneCall) 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); ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule break innerloop; //we don't need to search the other triggers in the same rule
@ -1253,7 +1401,7 @@ public class Rule implements Comparable<Rule>
} }
} }
} }
return ruleCandidates; return ruleCandidates;
} }
@ -1279,6 +1427,26 @@ public class Rule implements Comparable<Rule>
return ruleCandidates; return ruleCandidates;
} }
public static ArrayList<Rule> findRuleCandidates(Trigger.Trigger_Enum triggerType)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == triggerType)
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
}
}
}
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByActivityDetection() public static ArrayList<Rule> findRuleCandidatesByActivityDetection()
{ {

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package com.jens.automation2;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log; import android.util.Log;
import android.widget.Toast;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
@ -12,6 +13,10 @@ import java.util.Locale;
public class Action public class Action
{ {
public static final String actionParameter2Split = "ap2split";
public static final String intentPairSeperator = "intPairSplit";
public static final String vibrateSeparator = ",";
public enum Action_Enum { public enum Action_Enum {
setWifi, setWifi,
setBluetooth, setBluetooth,
@ -33,6 +38,8 @@ public class Action
speakText, speakText,
playMusic, playMusic,
setScreenBrightness, setScreenBrightness,
playSound,
vibrate,
sendTextMessage; sendTextMessage;
public String getFullName(Context context) public String getFullName(Context context)
@ -79,6 +86,8 @@ public class Action
return context.getResources().getString(R.string.waitBeforeNextAction); return context.getResources().getString(R.string.waitBeforeNextAction);
case wakeupDevice: case wakeupDevice:
return context.getResources().getString(R.string.wakeupDevice); return context.getResources().getString(R.string.wakeupDevice);
case vibrate:
return context.getResources().getString(R.string.vibrate);
case setAirplaneMode: case setAirplaneMode:
return context.getResources().getString(R.string.airplaneMode); return context.getResources().getString(R.string.airplaneMode);
case setDataConnection: case setDataConnection:
@ -87,6 +96,8 @@ public class Action
return context.getResources().getString(R.string.actionSpeakText); return context.getResources().getString(R.string.actionSpeakText);
case playMusic: case playMusic:
return context.getResources().getString(R.string.actionPlayMusic); return context.getResources().getString(R.string.actionPlayMusic);
case playSound:
return context.getResources().getString(R.string.playSound);
case sendTextMessage: case sendTextMessage:
return context.getResources().getString(R.string.sendTextMessage); return context.getResources().getString(R.string.sendTextMessage);
case setScreenBrightness: case setScreenBrightness:
@ -210,9 +221,13 @@ public class Action
{ {
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.wakeupDevice)); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.wakeupDevice));
} }
else if(this.getAction().equals(Action_Enum.playSound))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.playSound));
}
else else
returnString.append(action.toString()); returnString.append(action.toString());
if(this.getAction().equals(Action_Enum.triggerUrl)) if(this.getAction().equals(Action_Enum.triggerUrl))
{ {
String[] components = parameter2.split(";"); String[] components = parameter2.split(";");
@ -226,6 +241,10 @@ public class Action
else else
returnString.append(": " + components[0]); 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)) else if(this.getAction().equals(Action_Enum.sendTextMessage))
{ {
String[] components = parameter2.split(Actions.smsSeparator); String[] components = parameter2.split(Actions.smsSeparator);
@ -320,84 +339,91 @@ public class Action
} }
public void run(Context context, boolean toggleActionIfPossible) public void run(Context context, boolean toggleActionIfPossible)
{ {
switch(this.getAction()) try
{ {
case changeSoundProfile: switch(this.getAction())
/* {
* Old version. Those checks should not be necessary anymore. Also they didn't work case changeSoundProfile:
* because profiles were created with names like silent, vibrate and normal. /*
*/ * Old version. Those checks should not be necessary anymore. Also they didn't work
// if(this.getParameter2().equals("silent")) * because profiles were created with names like silent, vibrate and normal.
// 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()); Profile p = Profile.getByName(this.getParameter2());
if(p != null) if (p != null)
p.activate(context); p.activate(context);
// }
break; break;
case triggerUrl: case triggerUrl:
triggerUrl(context); triggerUrl(context);
break; break;
case setBluetooth: case setBluetooth:
Actions.setBluetooth(context, getParameter1(), toggleActionIfPossible); Actions.setBluetooth(context, getParameter1(), toggleActionIfPossible);
break; break;
case setUsbTethering: case setUsbTethering:
Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible); Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible);
break; break;
case setWifi: case setWifi:
Actions.setWifi(context, getParameter1(), toggleActionIfPossible); Actions.setWifi(context, getParameter1(), toggleActionIfPossible);
break; break;
case setWifiTethering: case setWifiTethering:
Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible); Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible);
break; break;
case setDisplayRotation: case setDisplayRotation:
Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible); Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible);
break; break;
case startOtherActivity: case startOtherActivity:
Actions.startOtherActivity(getParameter2()); Actions.startOtherActivity(getParameter1(), getParameter2());
break; break;
case waitBeforeNextAction: case waitBeforeNextAction:
Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2())); Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2()));
break; break;
case wakeupDevice: case wakeupDevice:
Actions.wakeupDevice(Long.parseLong(this.getParameter2())); Actions.wakeupDevice(Long.parseLong(this.getParameter2()));
// wakeupDevice() will create a seperate thread. That'll take some time, we wait 100ms. // wakeupDevice() will create a seperate thread. That'll take some time, we wait 100ms.
try try
{ {
Thread.sleep(100); Thread.sleep(100);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
break; break;
case setAirplaneMode: case setAirplaneMode:
Actions.setAirplaneMode(this.getParameter1(), toggleActionIfPossible); Actions.setAirplaneMode(this.getParameter1(), toggleActionIfPossible);
break; break;
case setDataConnection: case setDataConnection:
Actions.MobileDataStuff.setDataConnection(this.getParameter1(), toggleActionIfPossible); Actions.MobileDataStuff.setDataConnection(this.getParameter1(), toggleActionIfPossible);
break; break;
case speakText: case speakText:
Actions.speakText(this.getParameter2()); Actions.speakText(this.getParameter2());
break; break;
case playMusic: case playMusic:
Actions.playMusic(this.getParameter1(), toggleActionIfPossible); Actions.playMusic(this.getParameter1(), toggleActionIfPossible);
break; break;
case sendTextMessage: case sendTextMessage:
Actions.sendTextMessage(context, this.getParameter2().split(Actions.smsSeparator)); Actions.sendTextMessage(context, this.getParameter2().split(Actions.smsSeparator));
break; break;
case setScreenBrightness: case setScreenBrightness:
Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2())); Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2()));
break; break;
default: case vibrate:
Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3); Actions.vibrate(getParameter1(), getParameter2());
break; break;
case playSound:
Actions.playSound(getParameter1(), getParameter2());
break;
default:
Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3);
break;
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "ActionExecution", Log.getStackTraceString(e), 1);
Toast.makeText(context, context.getResources().getString(R.string.errorRunningRule), Toast.LENGTH_LONG).show();
} }
} }
@ -433,7 +459,7 @@ public class Action
} }
catch(Exception e) 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);
} }
} }
@ -464,62 +490,24 @@ public class Action
while(attempts <= Settings.httpAttempts && response.equals("httpError")) while(attempts <= Settings.httpAttempts && response.equals("httpError"))
{ {
Miscellaneous.logEvent("i", "HTTP Request", "Attempt " + String.valueOf(attempts++) + " of " + String.valueOf(Settings.httpAttempts), 3); 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"))
// Either thorough checking or no encryption response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword);
if(!Settings.httpAcceptAllCertificates | !urlString.toLowerCase(Locale.getDefault()).contains("https")) else
// { response = Miscellaneous.downloadURLwithoutCertificateChecking(urlString, urlUsername, urlPassword);
// URL url = new URL(urlString);
// URLConnection urlConnection = url.openConnection(); try
// urlConnection.setReadTimeout(Settings.httpAttemptsTimeout * 1000); {
// InputStream in = urlConnection.getInputStream(); Thread.sleep(Settings.httpAttemptGap * 1000);
// response = Miscellaneous.convertStreamToString(in); }
catch (InterruptedException e1)
response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword); {
// } Miscellaneous.logEvent("w", "HTTP RESULT", "Failed to pause between HTTP requests.", 5);
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);
}
// }
} }
// Miscellaneous.logEvent("i", "HTTPS RESULT", response, 3); Miscellaneous.logEvent("i", "HTTPS RESULT", response, 5);
return response; return response;
} }

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -19,12 +19,13 @@ import androidx.annotation.NonNull;
import com.jens.automation2.Action.Action_Enum; import com.jens.automation2.Action.Action_Enum;
public class ActivityEditSendTextMessage extends Activity public class ActivityManageActionSendTextMessage extends Activity
{ {
Button bSaveSendTextMessage, bImportNumberFromContacts; Button bSaveSendTextMessage, bImportNumberFromContacts;
EditText etPhoneNumber, etSendTextMessage; EditText etPhoneNumber, etSendTextMessage;
protected final static int requestCodeForContactsPermissions = 9876; protected final static int requestCodeForContactsPermissions = 9876;
protected final static int requestCodeGetContact = 3235;
// private String existingUrl = ""; // private String existingUrl = "";
@ -35,7 +36,7 @@ public class ActivityEditSendTextMessage extends Activity
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(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); etSendTextMessage = (EditText)findViewById(R.id.etSendTextMessage);
etPhoneNumber = (EditText)findViewById(R.id.etPhoneNumber); etPhoneNumber = (EditText)findViewById(R.id.etPhoneNumber);
@ -67,7 +68,7 @@ public class ActivityEditSendTextMessage extends Activity
@Override @Override
public void onClick(View view) public void onClick(View view)
{ {
if(!ActivityPermissions.havePermission("android.permission.READ_CONTACTS", ActivityEditSendTextMessage.this)) if(!ActivityPermissions.havePermission("android.permission.READ_CONTACTS", ActivityManageActionSendTextMessage.this))
{ {
requestPermissions("android.permission.READ_CONTACTS"); requestPermissions("android.permission.READ_CONTACTS");
} }
@ -76,30 +77,20 @@ public class ActivityEditSendTextMessage extends Activity
} }
}); });
ActivityEditSendTextMessage.edit = getIntent().getBooleanExtra("edit", false); ActivityManageActionSendTextMessage.edit = getIntent().getBooleanExtra("edit", false);
if(edit) if(edit)
{ {
String[] parameters = ActivityEditSendTextMessage.resultingAction.getParameter2().split(Actions.smsSeparator); String[] parameters = ActivityManageActionSendTextMessage.resultingAction.getParameter2().split(Actions.smsSeparator);
etPhoneNumber.setText(parameters[0]); etPhoneNumber.setText(parameters[0]);
etSendTextMessage.setText(parameters[1]); etSendTextMessage.setText(parameters[1]);
} }
// String url = getIntent().getStringExtra("urlToTrigger");
// if(url != null)
// existingUrl = url;
} }
private void backToRuleManager() private void backToRuleManager()
{ {
// Intent returnIntent = new Intent();
// returnIntent.putExtra("urlToTrigger", existingUrl);
// setResult(RESULT_OK, returnIntent);
if(edit && resultingAction != null) if(edit && resultingAction != null)
{ {
ActivityEditSendTextMessage.resultingAction.setParameter2(etPhoneNumber.getText().toString() + Actions.smsSeparator + etSendTextMessage.getText().toString()); ActivityManageActionSendTextMessage.resultingAction.setParameter2(etPhoneNumber.getText().toString() + Actions.smsSeparator + etSendTextMessage.getText().toString());
} }
setResult(RESULT_OK); setResult(RESULT_OK);
@ -145,16 +136,14 @@ public class ActivityEditSendTextMessage extends Activity
private void openContactsDialogue() 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, requestCodeGetContact);
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(intent, 1000);
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) protected void onActivityResult(int requestCode, int resultCode, Intent data)
{ {
if(requestCode == 1000) if(requestCode == requestCodeGetContact)
{ {
if(resultCode == Activity.RESULT_OK) if(resultCode == Activity.RESULT_OK)
{ {
@ -162,7 +151,7 @@ public class ActivityEditSendTextMessage extends Activity
String name = null; String name = null;
Uri uri = data.getData(); Uri uri = data.getData();
Cursor cursor = ActivityEditSendTextMessage.this.getContentResolver().query(uri, null, null, null, null); Cursor cursor = ActivityManageActionSendTextMessage.this.getContentResolver().query(uri, null, null, null, null);
if (cursor.moveToFirst()) if (cursor.moveToFirst())
{ {

View File

@ -10,7 +10,7 @@ import android.widget.Toast;
import com.jens.automation2.Action.Action_Enum; import com.jens.automation2.Action.Action_Enum;
public class ActivityEditSpeakText extends Activity public class ActivityManageActionSpeakText extends Activity
{ {
private Button bSaveSpeakText; private Button bSaveSpeakText;
private EditText etSpeakText; private EditText etSpeakText;
@ -24,7 +24,7 @@ public class ActivityEditSpeakText extends Activity
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(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); etSpeakText = (EditText)findViewById(R.id.etTextToSpeak);
bSaveSpeakText = (Button)findViewById(R.id.bSaveTriggerUrl); bSaveSpeakText = (Button)findViewById(R.id.bSaveTriggerUrl);
@ -48,9 +48,9 @@ public class ActivityEditSpeakText extends Activity
} }
}); });
ActivityEditSpeakText.edit = getIntent().getBooleanExtra("edit", false); ActivityManageActionSpeakText.edit = getIntent().getBooleanExtra("edit", false);
if(edit) if(edit)
etSpeakText.setText(ActivityEditSpeakText.resultingAction.getParameter2()); etSpeakText.setText(ActivityManageActionSpeakText.resultingAction.getParameter2());
// String url = getIntent().getStringExtra("urlToTrigger"); // String url = getIntent().getStringExtra("urlToTrigger");
@ -66,7 +66,7 @@ public class ActivityEditSpeakText extends Activity
// setResult(RESULT_OK, returnIntent); // setResult(RESULT_OK, returnIntent);
if(edit && resultingAction != null) if(edit && resultingAction != null)
ActivityEditSpeakText.resultingAction.setParameter2(etSpeakText.getText().toString()); ActivityManageActionSpeakText.resultingAction.setParameter2(etSpeakText.getText().toString());
setResult(RESULT_OK); setResult(RESULT_OK);

View File

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

View File

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

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.location.LocationManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
@ -21,6 +22,10 @@ import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.Toast; import android.widget.Toast;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class ActivityManagePoi extends Activity public class ActivityManagePoi extends Activity
{ {
public LocationManager myLocationManager; public LocationManager myLocationManager;
@ -31,6 +36,11 @@ public class ActivityManagePoi extends Activity
Button bGetPosition, bSavePoi; Button bGetPosition, bSavePoi;
ImageButton ibShowOnMap; ImageButton ibShowOnMap;
EditText guiPoiName, guiPoiLatitude, guiPoiLongitude, guiPoiRadius; 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; private static ProgressDialog progressDialog;
@ -47,7 +57,7 @@ public class ActivityManagePoi extends Activity
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(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); myLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
bGetPosition = (Button)findViewById(R.id.bGetPosition); bGetPosition = (Button)findViewById(R.id.bGetPosition);
@ -104,7 +114,7 @@ public class ActivityManagePoi extends Activity
myLocationManager.removeUpdates(myLocationListenerGps); myLocationManager.removeUpdates(myLocationListenerGps);
ActivityMainPoi.poiToEdit = new PointOfInterest(); ActivityMainPoi.poiToEdit = new PointOfInterest();
ActivityMainPoi.poiToEdit.setLocation(new Location("POINT_LOCATION")); ActivityMainPoi.poiToEdit.setLocation(new Location("POINT_LOCATION"));
if(loadFormValuesToVariable()) if(loadFormValuesToVariable(false))
if(ActivityMainPoi.poiToEdit.create(this)) if(ActivityMainPoi.poiToEdit.create(this))
{ {
this.setResult(RESULT_OK); this.setResult(RESULT_OK);
@ -114,7 +124,7 @@ public class ActivityManagePoi extends Activity
private void changePoi() private void changePoi()
{ {
myLocationManager.removeUpdates(myLocationListenerGps); myLocationManager.removeUpdates(myLocationListenerGps);
if(loadFormValuesToVariable()) if(loadFormValuesToVariable(false))
if(ActivityMainPoi.poiToEdit.change(this)) if(ActivityMainPoi.poiToEdit.change(this))
{ {
this.setResult(RESULT_OK); this.setResult(RESULT_OK);
@ -150,47 +160,142 @@ public class ActivityManagePoi extends Activity
} }
else else
{ {
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGettingPositionWithProvider) + " " + provider1, 3); locationSearchStart = Calendar.getInstance();
myLocationManager.requestLocationUpdates(provider1, 500, Settings.satisfactoryAccuracyNetwork, myLocationListenerNetwork); 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); Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGettingPositionWithProvider) + " " + provider2, 3);
myLocationManager.requestLocationUpdates(provider2, 500, Settings.satisfactoryAccuracyGps, myLocationListenerGps); 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) if(locationGps != null)
{ {
myLocationManager.removeUpdates(myLocationListenerNetwork);
guiPoiLatitude.setText(String.valueOf(locationGps.getLatitude())); guiPoiLatitude.setText(String.valueOf(locationGps.getLatitude()));
guiPoiLongitude.setText(String.valueOf(locationGps.getLongitude())); guiPoiLongitude.setText(String.valueOf(locationGps.getLongitude()));
String text;
if(locationNetwork != null) if(locationNetwork != null)
{ {
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.comparing), 4); Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.comparing), 4);
double variance = locationGps.distanceTo(locationNetwork); double variance = locationGps.distanceTo(locationNetwork);
text = String.format(getResources().getString(R.string.distanceBetween), Math.round(variance));
String text = getResources().getString(R.string.distanceBetween) + " " + String.valueOf(Math.round(variance)) + " " + getResources().getString(R.string.radiusSuggestion); getRadiusConfirmationDialog(text, Math.round(variance) + 1).show();
// 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();
// }
} }
else else
{ {
progressDialog.dismiss(); text = String.format(getResources().getString(R.string.locationFound), defaultRadius);
myLocationManager.removeUpdates(myLocationListenerNetwork); getRadiusConfirmationDialog(text, defaultRadius).show();
guiPoiRadius.setText("250");
} }
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 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) private AlertDialog getNotificationDialog(String text)
@ -202,15 +307,6 @@ public class ActivityManagePoi extends Activity
@Override @Override
public void onClick(DialogInterface dialog, int which) 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); progressDialog = ProgressDialog.show(ActivityManagePoi.this, "", getResources().getString(R.string.gettingPosition), true, true);
getLocation(); getLocation();
} }
@ -221,8 +317,11 @@ public class ActivityManagePoi extends Activity
return alertDialog; 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); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{ {
@ -247,10 +346,31 @@ public class ActivityManagePoi extends Activity
return alertDialog; 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 public class MyLocationListenerGps implements LocationListener
{ {
@Override @Override
public void onLocationChanged(Location location) 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", "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); myLocationManager.removeUpdates(this);
locationGps = location; locationGps = location;
compareLocations(); evaluateLocationResults();
// } // }
} }
@ -287,66 +408,27 @@ public class ActivityManagePoi extends Activity
// TODO Auto-generated method stub // 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 public class MyLocationListenerNetwork implements LocationListener
{ {
@Override @Override
public void onLocationChanged(Location location) public void onLocationChanged(Location location)
{ {
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGotNetworkUpdate) + " " + String.valueOf(location.getAccuracy()), 3); Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGotNetworkUpdate) + " " + String.valueOf(location.getAccuracy()), 3);
myLocationManager.removeUpdates(this);
locationNetwork = location;
// Deactivate when accuracy reached // Deactivate when accuracy reached
// if(location.getAccuracy() < Settings.SATISFACTORY_ACCURACY_GPS) if(location.getAccuracy() <= Settings.satisfactoryAccuracyGps)
// { {
// String text = "Network position found. satisfactoryNetworkAccuracy of " + String.valueOf(Settings.SATISFACTORY_ACCURACY_NETWORK) + "m reached. Removing location updates..."; // Accuracy is so good that we don't need to wait for GPS result
// Miscellaneous.logEvent("i", "POI Manager", text); Miscellaneous.logEvent("i", "POI Manager", "Unsubscribing from network location updates.", 5);
myLocationManager.removeUpdates(this); myLocationManager.removeUpdates(myLocationListenerGps);
locationNetwork = location; }
compareLocations(); evaluateLocationResults();
// }
} }
@Override @Override
@ -369,7 +451,6 @@ public class ActivityManagePoi extends Activity
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
} }
public void editPoi(PointOfInterest poi) public void editPoi(PointOfInterest poi)
@ -380,19 +461,22 @@ public class ActivityManagePoi extends Activity
guiPoiRadius.setText(String.valueOf(poi.getRadius())); guiPoiRadius.setText(String.valueOf(poi.getRadius()));
} }
public boolean loadFormValuesToVariable() public boolean loadFormValuesToVariable(boolean checkOnlyCoordinates)
{ {
if(ActivityMainPoi.poiToEdit == null) if(ActivityMainPoi.poiToEdit == null)
ActivityMainPoi.poiToEdit = new PointOfInterest(); ActivityMainPoi.poiToEdit = new PointOfInterest();
if(guiPoiName.getText().length() == 0) if(!checkOnlyCoordinates)
{ {
Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidName), Toast.LENGTH_LONG).show(); if (guiPoiName.getText().length() == 0)
return false; {
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) if(ActivityMainPoi.poiToEdit.getLocation() == null)
ActivityMainPoi.poiToEdit.setLocation(new Location("POINT_LOCATION")); 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(); Toast.makeText(this, getResources().getString(R.string.pleaseEnterValidLongitude), Toast.LENGTH_LONG).show();
return false; 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; return true;
} }
private void showOnMap() private void showOnMap()
{ {
if(loadFormValuesToVariable()) if(loadFormValuesToVariable(true))
{ {
try try
{ {

View File

@ -82,7 +82,7 @@ public class ActivityManageProfile extends Activity
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(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); checkBoxChangeSoundMode = (CheckBox)findViewById(R.id.checkBoxChangeSoundMode);
checkBoxChangeVolumeMusicVideoGameMedia = (CheckBox)findViewById(R.id.checkBoxChangeVolumeMusicVideoGameMedia); checkBoxChangeVolumeMusicVideoGameMedia = (CheckBox)findViewById(R.id.checkBoxChangeVolumeMusicVideoGameMedia);

View File

@ -95,6 +95,17 @@ public class ActivityManageRule extends Activity
final static int requestCodeActionScreenBrightnessAdd = 401; final static int requestCodeActionScreenBrightnessAdd = 401;
final static int requestCodeActionScreenBrightnessEdit = 402; final static int requestCodeActionScreenBrightnessEdit = 402;
final static int requestCodeActionSendTextMessage = 7001; final static int requestCodeActionSendTextMessage = 7001;
final static int requestCodeTriggerNotificationAdd = 8000;
final static int requestCodeTriggerNfcNotificationEdit = 8001;
final static int requestCodeActionPlaySoundAdd = 501;
final static int requestCodeActionPlaySoundEdit = 502;
final static int requestCodeTriggerPhoneCallAdd = 601;
final static int requestCodeTriggerPhoneCallEdit = 602;
final static int requestCodeTriggerWifiAdd = 723;
final static int requestCodeTriggerWifiEdit = 724;
final static int requestCodeActionSendTextMessageAdd = 5001;
final static int requestCodeActionVibrateAdd = 801;
final static int requestCodeActionVibrateEdit = 802;
public static ActivityManageRule getInstance() public static ActivityManageRule getInstance()
{ {
@ -165,7 +176,7 @@ public class ActivityManageRule extends Activity
hideKeyboard(); hideKeyboard();
getActionTypeDialog().show(); getActionTypeDialog().show();
} }
}); });
cmdSaveRule.setOnClickListener(new OnClickListener() cmdSaveRule.setOnClickListener(new OnClickListener()
{ {
@ -220,32 +231,35 @@ public class ActivityManageRule extends Activity
Trigger selectedTrigger = (Trigger)triggerListView.getItemAtPosition(arg2); Trigger selectedTrigger = (Trigger)triggerListView.getItemAtPosition(arg2);
switch(selectedTrigger.getTriggerType()) 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: case timeFrame:
ActivityManageTimeFrame.editedTimeFrameTrigger = selectedTrigger; ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = selectedTrigger;
Intent timeFrameEditor = new Intent(ActivityManageRule.this, ActivityManageTimeFrame.class); Intent timeFrameEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerTimeFrame.class);
startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeEdit); startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeEdit);
break; break;
// case usb_host_connection:
// break;
// case wifiConnection:
// break;
case bluetoothConnection: case bluetoothConnection:
ActivityManageBluetoothTrigger.editedBluetoothTrigger = selectedTrigger; ActivityManageTriggerBluetooth.editedBluetoothTrigger = selectedTrigger;
Intent bluetoothEditor = new Intent(ActivityManageRule.this, ActivityManageBluetoothTrigger.class); Intent bluetoothEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerBluetooth.class);
startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothEdit); startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothEdit);
break; break;
case notification:
ActivityManageTriggerNotification.editedNotificationTrigger = selectedTrigger;
Intent notificationEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerNotification.class);
notificationEditor.putExtra("edit", true);
startActivityForResult(notificationEditor, requestCodeTriggerNfcNotificationEdit);
break;
case phoneCall:
ActivityManageTriggerPhoneCall.editedPhoneCallTrigger = selectedTrigger;
Intent phoneCallEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerPhoneCall.class);
phoneCallEditor.putExtra("edit", true);
startActivityForResult(phoneCallEditor, requestCodeTriggerPhoneCallEdit);
break;
case wifiConnection:
Intent wifiEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerWifi.class);
wifiEditor.putExtra("edit", true);
wifiEditor.putExtra("wifiState", selectedTrigger.getTriggerParameter());
wifiEditor.putExtra("wifiName", selectedTrigger.getTriggerParameter2());
startActivityForResult(wifiEditor, requestCodeTriggerWifiEdit);
break;
default: default:
break; break;
} }
@ -279,66 +293,48 @@ public class ActivityManageRule extends Activity
Action a = (Action)actionListView.getItemAtPosition(arg2); Action a = (Action)actionListView.getItemAtPosition(arg2);
switch(a.getAction()) switch(a.getAction())
{ {
// case changeSoundProfile:
// break;
// case disableScreenRotation:
// break;
// case enableScreenRotation:
// break;
// case setAirplaneMode:
// break;
case startOtherActivity: case startOtherActivity:
Intent intent = new Intent(ActivityManageRule.this, ActivityManageStartActivity.class); Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionStartActivity.class);
ActivityManageStartActivity.resultingAction = a; ActivityManageActionStartActivity.resultingAction = a;
intent.putExtra("edit", true); intent.putExtra("edit", true);
startActivityForResult(intent, requestCodeActionStartActivityEdit); startActivityForResult(intent, requestCodeActionStartActivityEdit);
break; break;
case triggerUrl: case triggerUrl:
Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityEditTriggerUrl.class); Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityManageActionTriggerUrl.class);
// activityEditTriggerUrlIntent.putExtra("urlToTrigger", a.getParameter2()); ActivityManageActionTriggerUrl.resultingAction = a;
ActivityEditTriggerUrl.resultingAction = a;
activityEditTriggerUrlIntent.putExtra("edit", true); activityEditTriggerUrlIntent.putExtra("edit", true);
startActivityForResult(activityEditTriggerUrlIntent, requestCodeActionTriggerUrlEdit); startActivityForResult(activityEditTriggerUrlIntent, requestCodeActionTriggerUrlEdit);
break; 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: case speakText:
Intent activitySpeakTextIntent = new Intent(ActivityManageRule.this, ActivityEditSpeakText.class); Intent activitySpeakTextIntent = new Intent(ActivityManageRule.this, ActivityManageActionSpeakText.class);
ActivityEditSpeakText.resultingAction = a; ActivityManageActionSpeakText.resultingAction = a;
activitySpeakTextIntent.putExtra("edit", true); activitySpeakTextIntent.putExtra("edit", true);
startActivityForResult(activitySpeakTextIntent, requestCodeActionSpeakTextEdit); startActivityForResult(activitySpeakTextIntent, requestCodeActionSpeakTextEdit);
break; break;
case sendTextMessage: case sendTextMessage:
Intent activitySendTextMessageIntent = new Intent(ActivityManageRule.this, ActivityEditSendTextMessage.class); Intent activitySendTextMessageIntent = new Intent(ActivityManageRule.this, ActivityManageActionSendTextMessage.class);
ActivityEditSendTextMessage.resultingAction = a; ActivityManageActionSendTextMessage.resultingAction = a;
activitySendTextMessageIntent.putExtra("edit", true); activitySendTextMessageIntent.putExtra("edit", true);
startActivityForResult(activitySendTextMessageIntent, requestCodeActionSendTextMessage); startActivityForResult(activitySendTextMessageIntent, requestCodeActionSendTextMessage);
break; break;
case setScreenBrightness: case setScreenBrightness:
Intent activityEditScreenBrightnessIntent = new Intent(ActivityManageRule.this, ActivityManageBrightnessSetting.class); Intent activityEditScreenBrightnessIntent = new Intent(ActivityManageRule.this, ActivityManageActionBrightnessSetting.class);
// ActivityEditTriggerUrl.resultingAction = a;
activityEditScreenBrightnessIntent.putExtra("autoBrightness", a.getParameter1()); activityEditScreenBrightnessIntent.putExtra("autoBrightness", a.getParameter1());
activityEditScreenBrightnessIntent.putExtra("brightnessValue", Integer.parseInt(a.getParameter2())); activityEditScreenBrightnessIntent.putExtra("brightnessValue", Integer.parseInt(a.getParameter2()));
startActivityForResult(activityEditScreenBrightnessIntent, requestCodeActionScreenBrightnessEdit); startActivityForResult(activityEditScreenBrightnessIntent, requestCodeActionScreenBrightnessEdit);
break; 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);
actionPlaySoundIntent.putExtra("actionParameter1", a.getParameter1());
actionPlaySoundIntent.putExtra("actionParameter2", a.getParameter2());
startActivityForResult(actionPlaySoundIntent, requestCodeActionPlaySoundEdit);
break;
default: default:
Miscellaneous.logEvent("w", "Edit action", "Editing of action type " + a.getAction().toString() + " not implemented, yet.", 4); Miscellaneous.logEvent("w", "Edit action", "Editing of action type " + a.getAction().toString() + " not implemented, yet.", 4);
break; break;
@ -466,6 +462,8 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.bluetooth)); items.add(new Item(typesLong[i].toString(), R.drawable.bluetooth));
else if(types[i].toString().equals(Trigger_Enum.headsetPlugged.toString())) else if(types[i].toString().equals(Trigger_Enum.headsetPlugged.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.headphone)); items.add(new Item(typesLong[i].toString(), R.drawable.headphone));
else if(types[i].toString().equals(Trigger_Enum.notification.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.notification));
else else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder)); items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
} }
@ -502,19 +500,27 @@ public class ActivityManageRule extends Activity
String[] booleanChoices = null; String[] booleanChoices = null;
if(triggerType == Trigger_Enum.pointOfInterest) if(triggerType == Trigger_Enum.pointOfInterest)
{ {
if(PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0) if(Miscellaneous.googleToBlameForLocation(false))
booleanChoices = new String[]{getResources().getString(R.string.entering), getResources().getString(R.string.leaving)}; {
ActivityMainScreen.openGoogleBlamingWindow();
return;
}
else else
{ {
Toast.makeText(myContext, getResources().getString(R.string.noPoisSpecified), Toast.LENGTH_LONG).show(); if (PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0)
return; booleanChoices = new String[]{getResources().getString(R.string.entering), getResources().getString(R.string.leaving)};
else
{
Toast.makeText(myContext, getResources().getString(R.string.noPoisSpecified), Toast.LENGTH_LONG).show();
return;
}
} }
} }
else if(triggerType == Trigger_Enum.timeFrame) else if(triggerType == Trigger_Enum.timeFrame)
{ {
newTrigger.setTriggerType(Trigger_Enum.timeFrame); newTrigger.setTriggerType(Trigger_Enum.timeFrame);
ActivityManageTimeFrame.editedTimeFrameTrigger = newTrigger; ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = newTrigger;
Intent timeFrameEditor = new Intent(myContext, ActivityManageTimeFrame.class); Intent timeFrameEditor = new Intent(myContext, ActivityManageTriggerTimeFrame.class);
startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeAdd); startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeAdd);
return; return;
} }
@ -525,15 +531,36 @@ public class ActivityManageRule extends Activity
else if(triggerType == Trigger_Enum.speed | triggerType == Trigger_Enum.noiseLevel | triggerType == Trigger_Enum.batteryLevel) 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)}; booleanChoices = new String[]{getResources().getString(R.string.exceeds), getResources().getString(R.string.dropsBelow)};
else if(triggerType == Trigger_Enum.wifiConnection) 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) else if(triggerType == Trigger_Enum.process_started_stopped)
booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)};
else if(triggerType == Trigger_Enum.notification)
{
newTrigger.setTriggerType(Trigger_Enum.notification);
Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNotification.class);
startActivityForResult(nfcEditor, requestCodeTriggerNotificationAdd);
return;
}
else if(triggerType == Trigger_Enum.airplaneMode) else if(triggerType == Trigger_Enum.airplaneMode)
booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)};
else if(triggerType == Trigger_Enum.roaming) else if(triggerType == Trigger_Enum.roaming)
booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)};
else if(triggerType == Trigger_Enum.phoneCall) 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) else if(triggerType == Trigger_Enum.activityDetection)
{ {
try try
@ -564,7 +591,7 @@ public class ActivityManageRule extends Activity
if(NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true)) if(NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true))
{ {
newTrigger.setTriggerType(Trigger_Enum.nfcTag); newTrigger.setTriggerType(Trigger_Enum.nfcTag);
Intent nfcEditor = new Intent(myContext, ActivityManageNfc.class); Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNfc.class);
startActivityForResult(nfcEditor, requestCodeTriggerNfcTagAdd); startActivityForResult(nfcEditor, requestCodeTriggerNfcTagAdd);
return; return;
} }
@ -575,8 +602,8 @@ public class ActivityManageRule extends Activity
Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();; Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();;
newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection); newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection);
ActivityManageBluetoothTrigger.editedBluetoothTrigger = newTrigger; ActivityManageTriggerBluetooth.editedBluetoothTrigger = newTrigger;
Intent bluetoothEditor = new Intent(myContext, ActivityManageBluetoothTrigger.class); Intent bluetoothEditor = new Intent(myContext, ActivityManageTriggerBluetooth.class);
startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothAdd); startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothAdd);
return; return;
} }
@ -642,13 +669,6 @@ public class ActivityManageRule extends Activity
String[] choices = (String[]) choicesList.toArray(new String[choicesList.size()]); String[] choices = (String[]) choicesList.toArray(new String[choicesList.size()]);
getTriggerNoiseDialog(myContext, choices).show(); 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)) else if(triggerType.equals(Trigger_Enum.wifiConnection))
{ {
newTrigger.setTriggerType(Trigger_Enum.wifiConnection); newTrigger.setTriggerType(Trigger_Enum.wifiConnection);
@ -659,7 +679,6 @@ public class ActivityManageRule extends Activity
progressDialog = ProgressDialog.show(myContext, null, getResources().getString(R.string.gettingListOfInstalledApplications), true, false); progressDialog = ProgressDialog.show(myContext, null, getResources().getString(R.string.gettingListOfInstalledApplications), true, false);
newTrigger.setTriggerType(Trigger_Enum.process_started_stopped); newTrigger.setTriggerType(Trigger_Enum.process_started_stopped);
new GenerateApplicationSelectionsDialogTask().execute(ActivityManageRule.this); new GenerateApplicationSelectionsDialogTask().execute(ActivityManageRule.this);
// getTriggerRunningProcessDialog1(myContext).show();
} }
else if(triggerType.equals(Trigger_Enum.phoneCall)) else if(triggerType.equals(Trigger_Enum.phoneCall))
{ {
@ -792,7 +811,7 @@ public class ActivityManageRule extends Activity
{ {
public void onClick(DialogInterface dialog, int whichButton) public void onClick(DialogInterface dialog, int whichButton)
{ {
newTrigger.setWifiName(input.getText().toString()); // newTrigger.setWifiName(input.getText().toString());
ruleToEdit.getTriggerSet().add(newTrigger); ruleToEdit.getTriggerSet().add(newTrigger);
refreshTriggerList(); refreshTriggerList();
} }
@ -971,7 +990,7 @@ public class ActivityManageRule extends Activity
protected String[] doInBackground(ActivityManageRule... params) protected String[] doInBackground(ActivityManageRule... params)
{ {
// Looper.prepare(); // Looper.prepare();
final String[] applicationArray = ActivityManageStartActivity.getApplicationNameListString(params[0]); final String[] applicationArray = ActivityManageActionStartActivity.getApplicationNameListString(params[0]);
return applicationArray; return applicationArray;
} }
@ -1018,7 +1037,7 @@ public class ActivityManageRule extends Activity
{ {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(myContext); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(myContext);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectPackageOfApplication)); alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectPackageOfApplication));
final String[] packageArray = ActivityManageStartActivity.getPackageListString(myContext, applicationName); final String[] packageArray = ActivityManageActionStartActivity.getPackageListString(myContext, applicationName);
alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener() alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener()
{ {
@Override @Override
@ -1038,7 +1057,7 @@ public class ActivityManageRule extends Activity
{ {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(myContext); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(myContext);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectActivityToBeStarted)); alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectActivityToBeStarted));
final String activityArray[] = ActivityManageStartActivity.getActivityListForPackageName(packageName); final String activityArray[] = ActivityManageActionStartActivity.getActivityListForPackageName(packageName);
alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener() alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener()
{ {
@Override @Override
@ -1066,7 +1085,7 @@ public class ActivityManageRule extends Activity
if(resultCode == RESULT_OK) if(resultCode == RESULT_OK)
{ {
//add TriggerUrl //add TriggerUrl
ruleToEdit.getActionSet().add(ActivityEditTriggerUrl.resultingAction); ruleToEdit.getActionSet().add(ActivityManageActionTriggerUrl.resultingAction);
this.refreshActionList(); this.refreshActionList();
} }
} }
@ -1081,7 +1100,7 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerTimeframeAdd) else if(requestCode == requestCodeTriggerTimeframeAdd)
{ {
//add TimeFrame //add TimeFrame
if(resultCode == RESULT_OK && ActivityManageTimeFrame.editedTimeFrameTrigger != null) if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null)
{ {
ruleToEdit.getTriggerSet().add(newTrigger); ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList(); this.refreshTriggerList();
@ -1092,19 +1111,38 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerTimeframeEdit) else if(requestCode == requestCodeTriggerTimeframeEdit)
{ {
//edit TimeFrame //edit TimeFrame
if(resultCode == RESULT_OK && ActivityManageTimeFrame.editedTimeFrameTrigger != null) if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null)
{ {
this.refreshTriggerList(); this.refreshTriggerList();
} }
else else
Miscellaneous.logEvent("w", "TimeFrameEdit", "No timeframe returned. Assuming abort.", 5); 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) else if(requestCode == requestCodeActionStartActivityAdd)
{ {
// manage start of other activity // manage start of other activity
if(resultCode == RESULT_OK) if(resultCode == RESULT_OK)
{ {
newAction = ActivityManageStartActivity.resultingAction; newAction = ActivityManageActionStartActivity.resultingAction;
ruleToEdit.getActionSet().add(newAction); ruleToEdit.getActionSet().add(newAction);
this.refreshActionList(); this.refreshActionList();
} }
@ -1114,7 +1152,7 @@ public class ActivityManageRule extends Activity
// manage start of other activity // manage start of other activity
if(resultCode == RESULT_OK) if(resultCode == RESULT_OK)
{ {
newAction = ActivityManageStartActivity.resultingAction; newAction = ActivityManageActionStartActivity.resultingAction;
// ruleToEdit.getActionSet().add(newAction); // ruleToEdit.getActionSet().add(newAction);
this.refreshActionList(); this.refreshActionList();
} }
@ -1122,21 +1160,63 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerNfcTagAdd) else if(requestCode == requestCodeTriggerNfcTagAdd)
{ {
//add TimeFrame //add TimeFrame
if(resultCode == RESULT_OK && ActivityManageNfc.generatedId != null) if(resultCode == RESULT_OK && ActivityManageTriggerNfc.generatedId != null)
{ {
newTrigger.setNfcTagId(ActivityManageNfc.generatedId); newTrigger.setNfcTagId(ActivityManageTriggerNfc.generatedId);
ruleToEdit.getTriggerSet().add(newTrigger); ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList(); this.refreshTriggerList();
} }
else else
Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5); Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5);
} }
else if(requestCode == requestCodeTriggerNotificationAdd)
{
//add notification
if(resultCode == RESULT_OK)
{
ruleToEdit.getTriggerSet().add(newTrigger);
newTrigger.setTriggerParameter2(
data.getStringExtra("app") + Trigger.triggerParameter2Split +
data.getStringExtra("titleDir") + Trigger.triggerParameter2Split +
data.getStringExtra("title") + Trigger.triggerParameter2Split +
data.getStringExtra("textDir") + Trigger.triggerParameter2Split +
data.getStringExtra("text")
);
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerNfcNotificationEdit)
{
if(resultCode == RESULT_OK)
{
newTrigger = ActivityManageTriggerNotification.resultingTrigger;
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerPhoneCallAdd)
{
if(resultCode == RESULT_OK)
{
ruleToEdit.getTriggerSet().add(newTrigger);
newTrigger.setTriggerParameter2(data.getStringExtra("triggerParameter2"));
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerPhoneCallEdit)
{
if(resultCode == RESULT_OK)
{
newTrigger = ActivityManageTriggerPhoneCall.resultingTrigger;
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeActionSpeakTextAdd) else if(requestCode == requestCodeActionSpeakTextAdd)
{ {
if(resultCode == RESULT_OK) if(resultCode == RESULT_OK)
{ {
//add SpeakText //add SpeakText
ruleToEdit.getActionSet().add(ActivityEditSpeakText.resultingAction); ruleToEdit.getActionSet().add(ActivityManageActionSpeakText.resultingAction);
this.refreshActionList(); this.refreshActionList();
} }
} }
@ -1145,14 +1225,14 @@ public class ActivityManageRule extends Activity
if(resultCode == RESULT_OK) if(resultCode == RESULT_OK)
{ {
//add SpeakText //add SpeakText
ruleToEdit.getActionSet().add(ActivityEditSendTextMessage.resultingAction); ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction);
this.refreshActionList(); this.refreshActionList();
} }
} }
else if(requestCode == requestCodeTriggerBluetoothAdd) else if(requestCode == requestCodeTriggerBluetoothAdd)
{ {
//add bluetooth trigger //add bluetooth trigger
if(resultCode == RESULT_OK && ActivityManageBluetoothTrigger.editedBluetoothTrigger != null) if(resultCode == RESULT_OK && ActivityManageTriggerBluetooth.editedBluetoothTrigger != null)
{ {
ruleToEdit.getTriggerSet().add(newTrigger); ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList(); this.refreshTriggerList();
@ -1163,7 +1243,7 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerBluetoothEdit) else if(requestCode == requestCodeTriggerBluetoothEdit)
{ {
//edit bluetooth trigger //edit bluetooth trigger
if(resultCode == RESULT_OK && ActivityManageBluetoothTrigger.editedBluetoothTrigger != null) if(resultCode == RESULT_OK && ActivityManageTriggerBluetooth.editedBluetoothTrigger != null)
{ {
this.refreshTriggerList(); this.refreshTriggerList();
} }
@ -1193,6 +1273,48 @@ public class ActivityManageRule extends Activity
this.refreshActionList(); 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)
{
newAction.setParameter1(data.getBooleanExtra("actionParameter1", false));
newAction.setParameter2(data.getStringExtra("actionParameter2"));
ruleToEdit.getActionSet().add(newAction);
this.refreshActionList();
}
}
else if(requestCode == requestCodeActionPlaySoundEdit)
{
if(resultCode == RESULT_OK)
{
if(data.hasExtra("actionParameter1"))
ruleToEdit.getActionSet().get(editIndex).setParameter1(data.getBooleanExtra("actionParameter1", false));
if(data.hasExtra("actionParameter2"))
ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra("actionParameter2"));
this.refreshActionList();
}
}
//TODO: Check with has data been changed or something like that. //TODO: Check with has data been changed or something like that.
/*try /*try
@ -1245,6 +1367,10 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.tune)); items.add(new Item(typesLong[i].toString(), R.drawable.tune));
else if(types[i].toString().equals(Action_Enum.setScreenBrightness.toString())) else if(types[i].toString().equals(Action_Enum.setScreenBrightness.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.brightness)); 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())) else if(types[i].toString().equals(Action_Enum.sendTextMessage.toString()))
{ {
// if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this)) // if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this))
@ -1254,159 +1380,163 @@ public class ActivityManageRule extends Activity
else else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder)); items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
} }
// = {
// new Item("Bluetooth", R.drawable.bluetooth),
// new Item("Wifi", R.drawable.wifi),
// new Item("...", 0), //no icon for this one
// };
// ListAdapter adapter = new ArrayAdapter<Item>(this, android.R.layout.select_dialog_item, android.R.id.text1, items) ListAdapter adapter = new ArrayAdapter<Item>(this, android.R.layout.select_dialog_item, android.R.id.text1, items)
ListAdapter adapter = new ArrayAdapter<Item>(this, android.R.layout.select_dialog_item, android.R.id.text1, items) {
{ public View getView(int position, View convertView, ViewGroup parent)
public View getView(int position, View convertView, ViewGroup parent) {
{ //User super class to create the View
//User super class to create the View View v = super.getView(position, convertView, parent);
View v = super.getView(position, convertView, parent);
TextView tv = (TextView)v.findViewById(android.R.id.text1);
//Put the image on the TextView TextView tv = (TextView)v.findViewById(android.R.id.text1);
tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0);
//Add margin between image and text (support various screen densities) //Put the image on the TextView
int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f); tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0);
tv.setCompoundDrawablePadding(dp5);
return v; //Add margin between image and text (support various screen densities)
} int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
}; tv.setCompoundDrawablePadding(dp5);
AlertDialog.Builder builder = new AlertDialog.Builder(this) return v;
.setTitle(getResources().getString(R.string.selectTypeOfAction)) }
.setAdapter(adapter, new DialogInterface.OnClickListener() };
{
public void onClick(DialogInterface dialog, int which) AlertDialog.Builder builder = new AlertDialog.Builder(this)
{ .setTitle(getResources().getString(R.string.selectTypeOfAction))
newAction = new Action(); .setAdapter(adapter, new DialogInterface.OnClickListener()
{
if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.triggerUrl.toString())) public void onClick(DialogInterface dialog, int which)
{
newAction = new Action();
if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.triggerUrl.toString()))
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.triggerUrl);
ActivityManageActionTriggerUrl.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityManageActionTriggerUrl.class);
startActivityForResult(editTriggerIntent, 1000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifi.toString()))
{
newAction.setAction(Action_Enum.setWifi);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
Toast.makeText(context, context.getResources().getString(R.string.android10WifiToggleNotice), Toast.LENGTH_LONG).show();
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString()))
{
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH))
Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();;
newAction.setAction(Action_Enum.setBluetooth);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setUsbTethering.toString()))
{
newAction.setAction(Action_Enum.setUsbTethering);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1)
Miscellaneous.messageBox(context.getResources().getString(R.string.warning), context.getResources().getString(R.string.usbTetheringFailForAboveGingerbread), context).show();
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifiTethering.toString()))
{
newAction.setAction(Action_Enum.setWifiTethering);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDisplayRotation.toString()))
{
newAction.setAction(Action_Enum.setDisplayRotation);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.changeSoundProfile.toString()))
{
if(Profile.getProfileCollection().size() > 0)
{ {
//launch other activity to enter a url and parameters; newAction.setAction(Action_Enum.changeSoundProfile);
newAction.setAction(Action_Enum.triggerUrl); getActionSoundProfileDialog(context).show();
ActivityEditTriggerUrl.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityEditTriggerUrl.class);
startActivityForResult(editTriggerIntent, 1000);
} }
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifi.toString())) else
Toast.makeText(context, getResources().getString(R.string.noProfilesCreateOneFirst), Toast.LENGTH_LONG).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.startOtherActivity.toString()))
{
newAction.setAction(Action_Enum.startOtherActivity);
Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionStartActivity.class);
startActivityForResult(intent, 3000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.waitBeforeNextAction.toString()))
{
newAction.setAction(Action_Enum.waitBeforeNextAction);
getActionWaitBeforeNextActionDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.wakeupDevice.toString()))
{
newAction.setAction(Action_Enum.wakeupDevice);
getActionWakeupDeviceDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setAirplaneMode.toString()))
{
newAction.setAction(Action_Enum.setAirplaneMode);
getActionParameter1Dialog(ActivityManageRule.this).show();
if(Build.VERSION.SDK_INT >= 17)
{ {
newAction.setAction(Action_Enum.setWifi); // Toast.makeText(context, getResources().getString(R.string.airplaneModeSdk17Warning), Toast.LENGTH_LONG).show();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Miscellaneous.messageBox(getResources().getString(R.string.airplaneMode), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
Toast.makeText(context, context.getResources().getString(R.string.android10WifiToggleNotice), Toast.LENGTH_LONG).show();
getActionParameter1Dialog(ActivityManageRule.this).show();
} }
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString())) }
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDataConnection.toString()))
{
newAction.setAction(Action_Enum.setDataConnection);
getActionParameter1Dialog(ActivityManageRule.this).show();
Miscellaneous.messageBox(getResources().getString(R.string.actionDataConnection), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.speakText.toString()))
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.speakText);
ActivityManageActionSpeakText.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityManageActionSpeakText.class);
startActivityForResult(editTriggerIntent, 5000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.sendTextMessage.toString()))
{
if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, "android.permission.SEND_SMS"))
{ {
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) //launch other activity to enter parameters;
Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();; newAction.setAction(Action_Enum.sendTextMessage);
newAction.setAction(Action_Enum.setBluetooth); ActivityManageActionSendTextMessage.resultingAction = null;
getActionParameter1Dialog(ActivityManageRule.this).show(); Intent editTriggerIntent = new Intent(context, ActivityManageActionSendTextMessage.class);
startActivityForResult(editTriggerIntent, requestCodeActionSendTextMessageAdd);
} }
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setUsbTethering.toString())) }
{ else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playMusic.toString()))
newAction.setAction(Action_Enum.setUsbTethering); {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) newAction.setAction(Action_Enum.playMusic);
Toast.makeText(context, context.getResources().getString(R.string.usbTetheringFailForAboveGingerbread), Toast.LENGTH_LONG).show(); ruleToEdit.getActionSet().add(newAction);
getActionParameter1Dialog(ActivityManageRule.this).show(); refreshActionList();
} }
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifiTethering.toString())) else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.vibrate.toString()))
{ {
newAction.setAction(Action_Enum.setWifiTethering); newAction.setAction(Action_Enum.vibrate);
getActionParameter1Dialog(ActivityManageRule.this).show(); Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionVibrate.class);
} startActivityForResult(intent, requestCodeActionVibrateAdd);
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDisplayRotation.toString())) }
{ else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setScreenBrightness.toString()))
newAction.setAction(Action_Enum.setDisplayRotation); {
getActionParameter1Dialog(ActivityManageRule.this).show(); newAction.setAction(Action_Enum.setScreenBrightness);
} Intent actionScreenBrightnessIntent = new Intent(context, ActivityManageActionBrightnessSetting.class);
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.changeSoundProfile.toString())) startActivityForResult(actionScreenBrightnessIntent, requestCodeActionScreenBrightnessAdd);
{ }
if(Profile.getProfileCollection().size() > 0) else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playSound.toString()))
{ {
newAction.setAction(Action_Enum.changeSoundProfile); newAction.setAction(Action_Enum.playSound);
getActionSoundProfileDialog(context).show(); Intent actionPlaySoundIntent = new Intent(context, ActivityManageActionPlaySound.class);
} startActivityForResult(actionPlaySoundIntent, requestCodeActionPlaySoundAdd);
else }
Toast.makeText(context, getResources().getString(R.string.noProfilesCreateOneFirst), Toast.LENGTH_LONG).show(); }
} });
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.startOtherActivity.toString()))
{ return builder.create();
newAction.setAction(Action_Enum.startOtherActivity);
Intent intent = new Intent(ActivityManageRule.this, ActivityManageStartActivity.class);
startActivityForResult(intent, 3000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.waitBeforeNextAction.toString()))
{
newAction.setAction(Action_Enum.waitBeforeNextAction);
getActionWaitBeforeNextActionDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.wakeupDevice.toString()))
{
newAction.setAction(Action_Enum.wakeupDevice);
getActionWakeupDeviceDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setAirplaneMode.toString()))
{
if(Build.VERSION.SDK_INT >= 17)
{
Toast.makeText(context, getResources().getString(R.string.airplaneModeSdk17Warning), Toast.LENGTH_LONG).show();
Miscellaneous.messageBox(getResources().getString(R.string.airplaneMode), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
newAction.setAction(Action_Enum.setAirplaneMode);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDataConnection.toString()))
{
newAction.setAction(Action_Enum.setDataConnection);
getActionParameter1Dialog(ActivityManageRule.this).show();
Miscellaneous.messageBox(getResources().getString(R.string.actionDataConnection), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.speakText.toString()))
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.speakText);
ActivityEditSpeakText.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityEditSpeakText.class);
startActivityForResult(editTriggerIntent, 5000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.sendTextMessage.toString()))
{
// if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this))
if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, "android.permission.SEND_SMS"))
{
//launch other activity to enter parameters;
newAction.setAction(Action_Enum.sendTextMessage);
ActivityEditSendTextMessage.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityEditSendTextMessage.class);
startActivityForResult(editTriggerIntent, 5001);
}
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playMusic.toString()))
{
newAction.setAction(Action_Enum.playMusic);
ruleToEdit.getActionSet().add(newAction);
refreshActionList();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setScreenBrightness.toString()))
{
newAction.setAction(Action_Enum.setScreenBrightness);
Intent actionScreenBrightnessIntent = new Intent(context, ActivityManageBrightnessSetting.class);
startActivityForResult(actionScreenBrightnessIntent, requestCodeActionScreenBrightnessAdd);
}
}
});
return builder.create();
} }
private AlertDialog getActionSoundProfileDialog(final Context myContext) private AlertDialog getActionSoundProfileDialog(final Context myContext)
{ {
@ -1508,63 +1638,6 @@ public class ActivityManageRule extends Activity
return alertDialog; 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) private AlertDialog getActionWaitBeforeNextActionDialog(final Context myContext)
{ {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@ import java.sql.Time;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
public class ActivityManageTimeFrame extends Activity public class ActivityManageTriggerTimeFrame extends Activity
{ {
Button bSaveTimeFrame; Button bSaveTimeFrame;
TimePicker startPicker, stopPicker; TimePicker startPicker, stopPicker;
@ -27,7 +27,7 @@ public class ActivityManageTimeFrame extends Activity
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.trigger_timeframe_editor); setContentView(R.layout.activity_manage_trigger_timeframe);
startPicker = (TimePicker)findViewById(R.id.tpTimeFrameStart); startPicker = (TimePicker)findViewById(R.id.tpTimeFrameStart);
stopPicker = (TimePicker)findViewById(R.id.tpTimeFrameStop); 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; package com.jens.automation2;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
@ -9,11 +10,18 @@ import com.jens.automation2.R.layout;
public class ActivitySettings extends PreferenceActivity public class ActivitySettings extends PreferenceActivity
{ {
ListPreference lpStartScreenOptionsValues; ListPreference lpStartScreenOptionsValues;
CheckBoxPreference chkPrefUpdateCheck;
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(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; package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo; import android.app.ActivityManager.RunningServiceInfo;
@ -37,9 +38,10 @@ public class AutomationService extends Service implements OnInitListener
protected TextToSpeech ttsEngine = null; protected TextToSpeech ttsEngine = null;
protected final static int notificationId = 1000; protected final static int notificationId = 1000;
protected final static int notificationIdRestrictions = 1005; protected final static int notificationIdRestrictions = 1005;
protected final static int notificationIdLocationRestriction = 1006;
final static String NOTIFICATION_CHANNEL_ID = "com.jens.automation2"; final static String NOTIFICATION_CHANNEL_ID = "com.jens.automation2";
final static String channelName = "Automation notifications"; final static String channelName = "Service notification";
protected static Notification myNotification; protected static Notification myNotification;
protected static NotificationCompat.Builder notificationBuilder = null; protected static NotificationCompat.Builder notificationBuilder = null;
@ -106,28 +108,28 @@ public class AutomationService extends Service implements OnInitListener
public boolean checkStartupRequirements(Context context, boolean startAtBoot) public boolean checkStartupRequirements(Context context, boolean startAtBoot)
{ {
if (!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, AutomationService.this)) // if (!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, AutomationService.this))
{ // {
/* // /*
Don't have permission to access external storage. This is a show stopper as // Don't have permission to access external storage. This is a show stopper as
the configuration file is stored on external storage. // the configuration file is stored on external storage.
*/ // */
Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4); // Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4);
// Toast.makeText(AutomationService.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show(); //// Toast.makeText(AutomationService.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName); // ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName);
return false; // return false;
} // }
if(Build.VERSION.SDK_INT >= 28) 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. 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); 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(); // 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; return false;
} }
} }
@ -154,7 +156,8 @@ public class AutomationService extends Service implements OnInitListener
{ {
Miscellaneous.logEvent("w", "AutoStart", "Service is started via boot. Settingsfile not available because storage is not mounted, yet. Waiting for 3 seconds.", 4); Miscellaneous.logEvent("w", "AutoStart", "Service is started via boot. Settingsfile not available because storage is not mounted, yet. Waiting for 3 seconds.", 4);
Thread.sleep(3000); Thread.sleep(3000);
} catch (InterruptedException e) }
catch (InterruptedException e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
@ -169,15 +172,13 @@ public class AutomationService extends Service implements OnInitListener
} }
//if still no POIs... //if still no POIs...
if (//PointOfInterest.getPointOfInterestCollection() == null | PointOfInterest.getPointOfInterestCollection().size() == 0 if (Rule.getRuleCollection() == null | Rule.getRuleCollection().size() == 0)
// &&
Rule.getRuleCollection() == null | Rule.getRuleCollection().size() == 0
)
{ {
Miscellaneous.logEvent("w", "AutomationService", context.getResources().getString(R.string.serviceWontStart), 1); Miscellaneous.logEvent("w", "AutomationService", context.getResources().getString(R.string.serviceWontStart), 1);
Toast.makeText(context, context.getResources().getString(R.string.serviceWontStart), Toast.LENGTH_LONG).show(); Toast.makeText(context, context.getResources().getString(R.string.serviceWontStart), Toast.LENGTH_LONG).show();
return false; return false;
} else }
else
{ {
return true; return true;
} }
@ -196,7 +197,7 @@ public class AutomationService extends Service implements OnInitListener
if (checkStartupRequirements(this, startAtBoot)) 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(); startUpRoutine();
@ -210,7 +211,7 @@ public class AutomationService extends Service implements OnInitListener
ActivityMainScreen.updateMainScreen(); ActivityMainScreen.updateMainScreen();
this.isRunning = true; 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(); Toast.makeText(this, this.getResources().getString(R.string.serviceStarted), Toast.LENGTH_LONG).show();
// ********** Test area ********** // ********** Test area **********
// Miscellaneous.logEvent("i", "setNetworkType", "bin hier.", 3); // Miscellaneous.logEvent("i", "setNetworkType", "bin hier.", 3);
@ -257,7 +258,8 @@ public class AutomationService extends Service implements OnInitListener
case reloadSettings: case reloadSettings:
Settings.readFromPersistentStorage(this); Settings.readFromPersistentStorage(this);
applySettingsAndRules(); applySettingsAndRules();
myLocationProvider.applySettingsAndRules(); if(myLocationProvider != null)
myLocationProvider.applySettingsAndRules();
break; break;
case updateNotification: case updateNotification:
this.updateNotification(); this.updateNotification();
@ -318,6 +320,7 @@ public class AutomationService extends Service implements OnInitListener
checkForTtsEngine(); checkForTtsEngine();
checkForPermissions(); checkForPermissions();
checkForRestrictedFeatures(); checkForRestrictedFeatures();
checkForMissingBackgroundLocationPermission();
Actions.context = this; Actions.context = this;
Actions.autoMationServerRef = this; Actions.autoMationServerRef = this;
@ -340,6 +343,9 @@ public class AutomationService extends Service implements OnInitListener
{ {
boolean displayNotification = false; boolean displayNotification = false;
String rule = "";
outerLoop:
for(Rule r : Rule.getRuleCollection()) for(Rule r : Rule.getRuleCollection())
{ {
if(r.isRuleActive()) if(r.isRuleActive())
@ -352,7 +358,11 @@ public class AutomationService extends Service implements OnInitListener
// r.setRuleActive(false); // r.setRuleActive(false);
// r.change(AutomationService.this); // r.change(AutomationService.this);
if(!displayNotification) if(!displayNotification)
{
displayNotification = true; displayNotification = true;
rule = r.getName();
break outerLoop;
}
} }
} }
} }
@ -360,15 +370,16 @@ public class AutomationService extends Service implements OnInitListener
if(displayNotification) 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); Intent intent = new Intent(AutomationService.this, ActivityPermissions.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0); PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
Miscellaneous.createDismissableNotification(getResources().getString(R.string.appRunningInLimitedMode), ActivityPermissions.notificationIdPermissions, pi);
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();
} }
} }
@ -385,11 +396,50 @@ public class AutomationService extends Service implements OnInitListener
Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class); Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0); PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
// Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), ActivityPermissions.notificationIdPermissions, pi); // Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), ActivityPermissions.notificationIdPermissions, pi);
Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), notificationIdRestrictions, 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
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdRestrictions, pi);
} }
} }
} }
protected void checkForMissingBackgroundLocationPermission()
{
if(Miscellaneous.googleToBlameForLocation(true))
{
Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
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
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
}
/*
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
if (BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor"))
{
if (Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest))
{
Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
Miscellaneous.createDismissableNotificationWithDelay(2200, getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
else
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
}
}
}*/
}
public static void startAutomationService(Context context, boolean startAtBoot) public static void startAutomationService(Context context, boolean startAtBoot)
{ {
if(!(isMyServiceRunning(context))) if(!(isMyServiceRunning(context)))
@ -407,7 +457,8 @@ public class AutomationService extends Service implements OnInitListener
private void stopRoutine() private void stopRoutine()
{ {
Log.i("STOP", "Stopping"); Miscellaneous.logEvent("i", "Service", "Stopping service...", 3);
// Log.i("STOP", "Stopping");
try try
{ {
myLocationProvider.stopLocationService(); myLocationProvider.stopLocationService();
@ -543,11 +594,11 @@ public class AutomationService extends Service implements OnInitListener
if(activePoi == null) if(activePoi == null)
{ {
PointOfInterest closestPoi = PointOfInterest.getClosestPOI(instance.getLocationProvider().getCurrentLocation()); 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 else
{ {
bodyText = "Active POI: " + activePoi.getName() + lastRuleString; bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + activePoi.getName() + lastRuleString;
} }
} }
catch(NullPointerException e) catch(NullPointerException e)
@ -555,9 +606,9 @@ public class AutomationService extends Service implements OnInitListener
if( if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) 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); bodyText = instance.getResources().getString(R.string.stillGettingPosition);
else else

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package com.jens.automation2; package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Notification; import android.app.Notification;
@ -7,6 +8,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.ContentProvider;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -14,18 +16,20 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder; import android.os.IBinder;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.provider.Settings.Secure; import android.provider.Settings.Secure;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import com.jens.automation2.location.LocationProvider; import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener; import com.jens.automation2.receivers.PhoneStatusListener;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
@ -44,14 +48,19 @@ import org.w3c.dom.Element;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader; import java.io.StringReader;
import java.lang.Thread.UncaughtExceptionHandler; import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -70,6 +79,9 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Scanner; import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
@ -81,11 +93,15 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import androidx.core.app.NotificationCompat;
import androidx.documentfile.provider.DocumentFile;
import static android.provider.CalendarContract.CalendarCache.URI;
import static com.jens.automation2.AutomationService.NOTIFICATION_CHANNEL_ID; import static com.jens.automation2.AutomationService.NOTIFICATION_CHANNEL_ID;
import static com.jens.automation2.AutomationService.channelName; import static com.jens.automation2.AutomationService.channelName;
public class Miscellaneous extends Service public class Miscellaneous extends Service
{ {
protected static String writeableFolderStringCache = null; protected static String writeableFolderStringCache = null;
public static final String lineSeparator = System.getProperty("line.separator"); public static final String lineSeparator = System.getProperty("line.separator");
@ -105,15 +121,6 @@ public class Miscellaneous extends Service
if(url.toLowerCase().contains("https")) if(url.toLowerCase().contains("https"))
{ {
connection = (HttpsURLConnection) urlObject.openConnection(); 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 else
connection = (HttpURLConnection) urlObject.openConnection(); connection = (HttpURLConnection) urlObject.openConnection();
@ -313,45 +320,74 @@ public class Miscellaneous extends Service
{ {
if(writeableFolderStringCache == null) if(writeableFolderStringCache == null)
{ {
String testPath = null; // Use the app-specific folder as new default.
File folder = null; writeableFolderStringCache = Miscellaneous.getAnyContext().getFilesDir().getAbsolutePath();
try File newConfigFile = new File(writeableFolderStringCache + "/" + XmlFileInterface.settingsFileName);
migration:
if (!newConfigFile.exists())
{ {
String[] foldersToTestArray = new String[] if (ActivityPermissions.havePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Miscellaneous.getAnyContext()))
{
Environment.getExternalStorageDirectory().getAbsolutePath(),
"/storage/emulated/0",
"/HWUserData",
"/mnt/sdcard"
};
for(String f : foldersToTestArray)
{ {
if (testFolder(f)) // We have the storage permission, probably because it's an old installation. Files should be migrated to app-specific folder.
String testPath = null;
File folder = null;
try
{ {
String pathToUse = f + "/" + Settings.folderName; String[] foldersToTestArray = new String[]
Miscellaneous.logEvent("i", "Path", "Using " + pathToUse + " to store settings and log.", 2); {
Environment.getExternalStorageDirectory().getAbsolutePath(),
"/storage/emulated/0",
"/HWUserData",
"/mnt/sdcard"
};
for (String f : foldersToTestArray)
{
// if (testFolder(f))
// {
String pathToUse = f + "/" + Settings.folderName;
// Toast.makeText(getAnyContext(), "Using " + pathToUse + " to store settings and log.", Toast.LENGTH_LONG).show(); // Toast.makeText(getAnyContext(), "Using " + pathToUse + " to store settings and log.", Toast.LENGTH_LONG).show();
return pathToUse; // Migrate existing files
File oldDirectory = new File(pathToUse);
File newDirectory = new File(writeableFolderStringCache);
File oldConfigFilePath = new File(pathToUse + "/" + XmlFileInterface.settingsFileName);
if (oldConfigFilePath.exists() && oldConfigFilePath.canWrite())
{
Miscellaneous.logEvent("i", "Path", "Found old path " + pathToUse + " for settings and logs. Migrating old files to new directory.", 2);
for (File fileToBeMoved : oldDirectory.listFiles())
{
File dstFile = new File(writeableFolderStringCache + "/" + fileToBeMoved.getName());
/*
For some stupid reason Android's file.moveTo can't move files between
mount points. That's why we have to copy it and delete the src if successful.
*/
if(copyFileUsingStream(fileToBeMoved, dstFile))
fileToBeMoved.delete();
}
String message = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.filesHaveBeenMovedTo), newDirectory.getAbsolutePath());
Miscellaneous.writeStringToFile(oldDirectory.getAbsolutePath() + "/readme.txt", message);
break migration;
}
// }
}
} catch (Exception e)
{
Log.w("getWritableFolder", folder + " not writable.");
} }
else
Miscellaneous.logEvent("e", "getWritableFolder", folder.getAbsolutePath() + " does not exist and could not be created.", 3);
} }
} }
catch(Exception e)
{
Log.w("getWritableFolder", folder + " not writable.");
}
// do not change to logEvent() - we can't write
Toast.makeText(getAnyContext(), "No writable folder could be found.", Toast.LENGTH_LONG).show();
Log.e("getWritableFolder", "No writable folder could be found.");
return null;
} }
else
return writeableFolderStringCache; return writeableFolderStringCache;
} }
protected final static String logFileName = "Automation_logfile.txt"; protected final static String logFileName = "Automation_logfile.txt";
@ -408,6 +444,25 @@ public class Miscellaneous extends Service
// Miscellaneous.logEvent("i", TAG, "isEmulator=" + isEmulator); // Miscellaneous.logEvent("i", TAG, "isEmulator=" + isEmulator);
return isEmulator; return isEmulator;
} }
public static boolean compare(String direction, String needle, String haystack)
{
switch(direction)
{
case Trigger.directionEquals:
return haystack.equalsIgnoreCase(needle);
case Trigger.directionNotEquals:
return !haystack.equalsIgnoreCase(needle);
case Trigger.directionContains:
return haystack.toLowerCase().contains(needle.toLowerCase());
case Trigger.directionStartsWith:
return haystack.toLowerCase().startsWith(needle.toLowerCase());
case Trigger.directionEndsWith:
return haystack.toLowerCase().endsWith(needle.toLowerCase());
default:
return false;
}
}
public static int compareTimes(Time time1, Time time2) public static int compareTimes(Time time1, Time time2)
{ {
@ -536,6 +591,26 @@ public class Miscellaneous extends Service
source = source.replace("[s]", String.valueOf(cal.get(Calendar.SECOND))); source = source.replace("[s]", String.valueOf(cal.get(Calendar.SECOND)));
source = source.replace("[ms]", String.valueOf(cal.get(Calendar.MILLISECOND))); 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); // Miscellaneous.logEvent("i", "URL after replace", source);
@ -735,10 +810,63 @@ public class Miscellaneous extends Service
return allHostsValid; return allHostsValid;
} }
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static void createDismissableNotificationWithDelay(long delay, String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
/*
Now what's this about?
From SDK 27 onwards you can only fire 1 notification per second:
https://developer.android.com/about/versions/oreo/android-8.1?hl=bn#notify
There are some situations where the service is just being started - resulting in a notification. But we have
additional need to inform the user about something and want to create another notification. That's why we have
to delay it for a moment, but don't want to hold off the main threat.
*/
class AsyncTaskCreateNotification extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... voids)
{
setDefaultBehaviour(this);
try
{
Thread.sleep(delay);
}
catch(Exception e)
{}
createDismissableNotification(textToDisplay, notificationId, pendingIntent);
return null;
}
}
AsyncTaskCreateNotification astCn = new AsyncTaskCreateNotification();
astCn.execute(null, null);
}
private static void setDefaultBehaviour(AsyncTask asyncTask)
{
// without this line debugger will - for some reason - skip all breakpoints in this class
if(android.os.Debug.isDebuggerConnected())
android.os.Debug.waitForDebugger();
// Thread.setDefaultUncaughtExceptionHandler(Miscellaneous.getUncaughtExceptionHandler(activityMainRef, true));
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static void createDismissableNotification(String textToDisplay, int notificationId, PendingIntent pendingIntent) public static void createDismissableNotification(String textToDisplay, int notificationId, PendingIntent pendingIntent)
{ {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
createDismissableNotificationSdk26(textToDisplay, notificationId, pendingIntent);
return;
}
NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder dismissableNotificationBuilder = createDismissableNotificationBuilder(pendingIntent); NotificationCompat.Builder dismissableNotificationBuilder = createDismissableNotificationBuilder(pendingIntent);
@ -750,50 +878,78 @@ public class Miscellaneous extends Service
mNotificationManager.notify(notificationId, dismissableNotification); mNotificationManager.notify(notificationId, dismissableNotification);
/*NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) /*NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher) // notification icon .setSmallIcon(R.drawable.ic_launcher) // notification icon
.setContentTitle("Notification!") // title for notification .setContentTitle("Notification!") // title for notification
.setContentText("Hello word") // message for notification .setContentText("Hello word") // message for notification
.setAutoCancel(true); // clear notification after click .setAutoCancel(true); // clear notification after click
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pi = PendingIntent.getActivity(this,0,intent,Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(pi); mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager = NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, dismissableNotification);*/ mNotificationManager.notify(0, dismissableNotification);*/
} }
/*protected static Notification.Builder createDismissableNotificationBuilder() static void createDismissableNotificationSdk26(String textToDisplay, int notificationId, PendingIntent pendingIntent)
{ {
Notification.Builder builder = new Notification.Builder(AutomationService.getInstance()); NotificationManager mNotificationManager = (NotificationManager) AutomationService.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
builder.setContentTitle("Automation");
builder.setSmallIcon(R.drawable.ic_launcher); NotificationCompat.Builder builder;
builder.setCategory(Notification.CATEGORY_EVENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Functionality warnings", NotificationManager.IMPORTANCE_HIGH);
// chan.setLightColor(Color.BLUE);
chan.enableVibration(false);
// chan.setSound(null, null);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
mNotificationManager.createNotificationChannel(chan);
builder = new NotificationCompat.Builder(AutomationService.getInstance(), NOTIFICATION_CHANNEL_ID);
}
else
builder = new NotificationCompat.Builder(AutomationService.getInstance());
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
// builder.setCategory(Notification.CATEGORY_SERVICE);
builder.setWhen(System.currentTimeMillis()); builder.setWhen(System.currentTimeMillis());
builder.setContentIntent(pendingIntent);
//static PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0); builder.setContentTitle(AutomationService.getInstance().getResources().getString(R.string.app_name));
builder.setOnlyAlertOnce(true);
//builder.setContentIntent(myPendingIntent); if(Settings.showIconWhenServiceIsRunning)
builder.setSmallIcon(R.drawable.ic_launcher);
// Notification defaultNotification = new Notification(); builder.setContentText(textToDisplay);
*//* Notification defaultNotification = builder.build(); builder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
defaultNotification.icon = R.drawable.ic_launcher; NotificationManager notificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
defaultNotification.when = System.currentTimeMillis(); notificationManager.notify(1, builder.build());
// defaultNotification.defaults |= Notification.DEFAULT_VIBRATE;
// defaultNotification.defaults |= Notification.DEFAULT_LIGHTS;
defaultNotification.flags |= Notification.FLAG_AUTO_CANCEL; // Intent notifyIntent = new Intent(context, notification.class);
// defaultNotification.flags |= Notification.FLAG_SHOW_LIGHTS; // notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
defaultNotification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; //
// pendingIntent.getIntentSender().g
// defaultNotification.ledARGB = Color.YELLOW; //
// defaultNotification.ledOnMS = 1500; // PendingIntent pendingIntent = PendingIntent.getActivities(context, 0,
// defaultNotification.ledOffMS = 1500; // new Intent[]{notifyIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
*//* //
return builder; // Notification notification = new Notification.Builder(Miscellaneous.getAnyContext())
}*/ // .setSmallIcon(android.R.drawable.ic_dialog_info)
// .setContentTitle("Automation")
// .setContentText(textToDisplay)
// .setAutoCancel(true)
// .setContentIntent(pendingIntent)
// .build();
// notification.defaults |= Notification.DEFAULT_SOUND;
// NotificationManager notificationManager =
// (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
// notificationManager.notify(1, notification);
}
protected static NotificationCompat.Builder createDismissableNotificationBuilder(PendingIntent myPendingIntent) protected static NotificationCompat.Builder createDismissableNotificationBuilder(PendingIntent myPendingIntent)
{ {
@ -803,7 +959,7 @@ public class Miscellaneous extends Service
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{ {
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW); NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_HIGH);
// chan.setLightColor(Color.BLUE); // chan.setLightColor(Color.BLUE);
// chan.enableVibration(false); // chan.enableVibration(false);
// chan.setSound(null, null); // chan.setSound(null, null);
@ -833,13 +989,21 @@ public class Miscellaneous extends Service
return builder; return builder;
} }
public static String explode(ArrayList<String> arrayList) public static String explode(String glue, ArrayList<String> arrayList)
{ {
StringBuilder builder = new StringBuilder(); if(arrayList != null)
for(String s : arrayList) {
builder.append(s); StringBuilder builder = new StringBuilder();
for (String s : arrayList)
builder.append(s + glue);
return builder.toString(); if (builder.length() > 0)
builder.delete(builder.length() - glue.length(), builder.length());
return builder.toString();
}
else
return "";
} }
public static boolean isGooglePlayInstalled(Context context) public static boolean isGooglePlayInstalled(Context context)
@ -1037,4 +1201,295 @@ public class Miscellaneous extends Service
return null; return null;
} }
} }
public static boolean copyFileUsingStream(File source, File dest) throws IOException
{
boolean returnValue = false;
InputStream is = null;
OutputStream os = null;
try
{
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
returnValue = true;
}
finally
{
is.close();
os.close();
}
return returnValue;
}
public static boolean copyDocumentFileToFile(DocumentFile src, File dst)
{
InputStream in = null;
OutputStream out = null;
String error = null;
try
{
in = Miscellaneous.getAnyContext().getContentResolver().openInputStream(src.getUri());
out = new FileOutputStream(dst);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
return true;
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return false;
// return error;
}
public static boolean copyFileToDocumentFile(File src, DocumentFile dst)
{
InputStream in = null;
OutputStream out = null;
String error = null;
try
{
in = new FileInputStream(src);
out = Miscellaneous.getAnyContext().getContentResolver().openOutputStream(dst.getUri());
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
return true;
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return false;
// return error;
}
/*public static String copyDocumentFile(String inputPath, String inputFile, Uri treeUri)
{
InputStream in = null;
OutputStream out = null;
String error = null;
DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
String extension = inputFile.substring(inputFile.lastIndexOf(".")+1,inputFile.length());
try
{
DocumentFile newFile = pickedDir.createFile("audio/"+extension, inputFile);
out = getActivity().getContentResolver().openOutputStream(newFile.getUri());
in = new FileInputStream(inputPath + inputFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return error;
}*/
public static boolean googleToBlameForLocation(boolean checkExistingRules)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
if (BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor"))
{
if(checkExistingRules)
{
if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest))
{
return true;
}
}
else
return true;
}
}
return false;
}
public static void zip(String[] _files, String zipFileName)
{
int BUFFER = 2048;
try
{
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(zipFileName);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
byte data[] = new byte[BUFFER];
for (int i = 0; i < _files.length; i++)
{
Log.v("Compress", "Adding: " + _files[i]);
FileInputStream fi = new FileInputStream(_files[i]);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(_files[i].substring(_files[i].lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1)
{
out.write(data, 0, count);
}
origin.close();
}
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void unzip(String _zipFile, String _targetLocation)
{
int BUFFER = 2048;
try
{
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null)
{
//create dir if required while unzipping
if (ze.isDirectory())
{
// dirChecker(ze.getName());
}
else
{
FileOutputStream fout = new FileOutputStream(_targetLocation + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read())
{
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
}
catch (Exception e)
{
System.out.println(e);
}
}
public static void sendEmail(Context context, String targetAddress, String subject, String message, Uri fileAttachment)
{
try
{
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{targetAddress});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
if (fileAttachment != null)
{
emailIntent.putExtra(Intent.EXTRA_STREAM, fileAttachment);
}
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
context.startActivity(Intent.createChooser(emailIntent, "Sending email..."));
}
catch (Throwable t)
{
Toast.makeText(context, "Request failed try again: "+ t.toString(), Toast.LENGTH_LONG).show();
}
}
public static boolean doesActivityExist(Intent intent, Context context)
{
return intent.resolveActivityInfo(context.getPackageManager(), 0) != null;
}
public static boolean isRegularExpression(String regex)
{
try
{
"compareString".matches(regex); //will cause expection if no valid regex
return true;
}
catch(java.util.regex.PatternSyntaxException e)
{
}
return false;
}
public static boolean comparePhoneNumbers(String number1, String number2)
{
/* To be activated when Android S SDK comes out
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.Q)
{
TelephonyManager tm = (TelephonyManager)Miscellaneous.getAnyContext().getSystemService(Context.TELEPHONY_SERVICE);
return PhoneNumberUtils.areSamePhoneNumber(number1, number2, tm.getNetworkCountryIso());
}
else*/
return PhoneNumberUtils.compare(number1, number2);
}
} }

View File

@ -10,10 +10,8 @@ import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File; import java.io.File;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
@ -70,9 +68,15 @@ public class News
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
String newsContent; String newsContent;
String filePath = context.getFilesDir() + "/appNews.xml"; String newsFileName = "appNews.xml";
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.pollNewsEveryXDays * 24 * 60 * 60 * 1000)) String filePath = context.getCacheDir() + "/" + newsFileName;
File oldFilePath = new File(context.getFilesDir() + "/" + newsFileName);
if(oldFilePath.exists())
oldFilePath.delete();
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == Settings.default_lastNewsPolltime || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.newsDisplayForXDays * 24 * 60 * 60 * 1000))
{ {
String newsUrl = "https://server47.de/automation/appNews.php"; String newsUrl = "https://server47.de/automation/appNews.php";
newsContent = Miscellaneous.downloadURL(newsUrl, null, null); newsContent = Miscellaneous.downloadURL(newsUrl, null, null);
@ -82,14 +86,14 @@ public class News
{ {
Settings.lastNewsPolltime = now.getTimeInMillis(); Settings.lastNewsPolltime = now.getTimeInMillis();
Settings.writeSettings(context); Settings.writeSettings(context);
Miscellaneous.logEvent("i", "appNews.xml", "File stored to " + filePath, 5); Miscellaneous.logEvent("i", newsFileName, "File stored to " + filePath, 5);
} }
} }
else else
{ {
// Just read local cache file // Just read local cache file
newsContent = Miscellaneous.readFileToString(filePath); newsContent = Miscellaneous.readFileToString(filePath);
Miscellaneous.logEvent("i", "appNews.xml", "Using cache to retrieve news: " + filePath, 5); Miscellaneous.logEvent("i", newsFileName, "Using cache to retrieve news: " + filePath, 5);
} }
ArrayList<News> returnList = new ArrayList<>(); ArrayList<News> returnList = new ArrayList<>();
@ -245,7 +249,7 @@ public class News
try try
{ {
Calendar limit = Calendar.getInstance(); Calendar limit = Calendar.getInstance();
limit.add(Calendar.DAY_OF_MONTH, -Settings.pollNewsEveryXDays); limit.add(Calendar.DAY_OF_MONTH, -Settings.newsPollEveryXDays);
return downloadNews(contexts[0], limit); return downloadNews(contexts[0], limit);
} }
catch(Exception e) catch(Exception e)
@ -258,7 +262,18 @@ public class News
@Override @Override
protected void onPostExecute(ArrayList arrayList) protected void onPostExecute(ArrayList arrayList)
{ {
ActivityMainScreen.getActivityMainScreenInstance().processNewsResult(arrayList); try
{
ActivityMainScreen.getActivityMainScreenInstance().processNewsResult(arrayList);
}
catch(NullPointerException e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the already downloded news, probably ActivityMainScreen isn't currently shown: " + Log.getStackTraceString(e), 2);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the already downloded news: " + Log.getStackTraceString(e), 2);
}
} }
} }
} }

View File

@ -491,9 +491,17 @@ public class PointOfInterest implements Comparable<PointOfInterest>
AutomationService service = AutomationService.getInstance(); AutomationService service = AutomationService.getInstance();
if (service != null) if (service != null)
{ {
service.applySettingsAndRules(); try
//Easiest way to check for changes in location, reset the last known location. {
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true); 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; return true;
@ -530,14 +538,18 @@ public class PointOfInterest implements Comparable<PointOfInterest>
PointOfInterest.writePoisToFile(); PointOfInterest.writePoisToFile();
AutomationService service = AutomationService.getInstance(); AutomationService service = AutomationService.getInstance();
if(service != null)
try
{ {
service.applySettingsAndRules(); service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location. //Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true); 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; 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)); String text = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.overlapBetweenPois), otherPoi.getName(), String.valueOf(overlap));
Miscellaneous.logEvent("w", "POI", text, 2); Miscellaneous.logEvent("w", "POI", text, 2);
// Miscellaneous.messageBox("POI", text, Miscellaneous.getAnyContext()).show();
Toast.makeText(Miscellaneous.getAnyContext(), text, Toast.LENGTH_LONG).show(); Toast.makeText(Miscellaneous.getAnyContext(), text, Toast.LENGTH_LONG).show();
return false; return false;
} }

View File

@ -56,7 +56,7 @@ public class ReceiverCoordinator
} }
catch (ClassNotFoundException e) catch (ClassNotFoundException e)
{ {
e.printStackTrace(); // e.printStackTrace();
allImplementers = new Class[] { allImplementers = new Class[] {
AlarmListener.class, AlarmListener.class,
@ -145,7 +145,10 @@ public class ReceiverCoordinator
// startCellLocationChangedReceiver // startCellLocationChangedReceiver
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed))) if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed)))
CellLocationChangedReceiver.startCellLocationChangedReceiver(); {
if(!Miscellaneous.googleToBlameForLocation(true))
CellLocationChangedReceiver.startCellLocationChangedReceiver();
}
// startBatteryReceiver // startBatteryReceiver
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel)) if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel))
@ -268,34 +271,46 @@ public class ReceiverCoordinator
ProcessListener.stopProcessListener(AutomationService.getInstance()); ProcessListener.stopProcessListener(AutomationService.getInstance());
} }
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection)) if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor"))
{ {
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null); if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
if(isRunning)
{ {
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4); Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
boolean haveAllPerms = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "haveAllPermission", null);
if(haveAllPerms) if (runResult instanceof Boolean)
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "restartActivityDetectionReceiver", null); {
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "restartActivityDetectionReceiver", null);
// ActivityDetectionReceiver.restartActivityDetectionReceiver(); // ActivityDetectionReceiver.restartActivityDetectionReceiver();
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
} }
else else
{ {
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4); Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
boolean haveAllPerms = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "haveAllPermission", null); if (runResult instanceof Boolean)
if(haveAllPerms) {
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "startActivityDetectionReceiver", null); boolean isRunning = (Boolean) runResult;
// ActivityDetectionReceiver.startActivityDetectionReceiver(); if (isRunning)
} {
} Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
else Miscellaneous.runMethodReflective(activityDetectionClassPath, "stopActivityDetectionReceiver", null);
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "stopActivityDetectionReceiver", null);
// ActivityDetectionReceiver.stopActivityDetectionReceiver(); // ActivityDetectionReceiver.stopActivityDetectionReceiver();
}
}
} }
} }

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -12,8 +13,13 @@ public class Settings implements SharedPreferences
{ {
public static final int rulesThatHaveBeenRanHistorySize = 10; public static final int rulesThatHaveBeenRanHistorySize = 10;
public final static int lockSoundChangesInterval = 15; public final static int lockSoundChangesInterval = 15;
public static final int pollNewsEveryXDays = 7; public static final int newsPollEveryXDays = 3;
public static final int newsDisplayForXDays = 3;
public static final int updateCheckFrequencyDays = 7;
public static final String folderName = "Automation"; public static final String folderName = "Automation";
public static final String zipFileName = "automation.zip";
public static final String constNewsOptInDone ="newsOptInDone";
public static long minimumDistanceChangeForGpsUpdate; public static long minimumDistanceChangeForGpsUpdate;
public static long minimumDistanceChangeForNetworkUpdate; public static long minimumDistanceChangeForNetworkUpdate;
@ -54,12 +60,18 @@ public class Settings implements SharedPreferences
public static int activityDetectionRequiredProbability; public static int activityDetectionRequiredProbability;
public static boolean privacyLocationing; public static boolean privacyLocationing;
public static int startScreen; public static int startScreen;
public static int tabsPlacement;
public static boolean executeRulesAndProfilesWithSingleClick; public static boolean executeRulesAndProfilesWithSingleClick;
public static boolean displayNewsOnMainScreen; public static boolean displayNewsOnMainScreen;
public static boolean automaticUpdateCheck;
public static boolean lockSoundChanges; public static boolean lockSoundChanges;
public static boolean noticeAndroid9MicrophoneShown; public static boolean noticeAndroid9MicrophoneShown;
public static boolean noticeAndroid10WifiShown; public static boolean noticeAndroid10WifiShown;
public static long lastNewsPolltime; public static long lastNewsPolltime;
public static long lastUpdateCheck;
public static ArrayList<String> whatHasBeenDone;
/* /*
Generic settings valid for all installations and not changable Generic settings valid for all installations and not changable
@ -105,10 +117,13 @@ public class Settings implements SharedPreferences
protected static final int default_activityDetectionRequiredProbability = 75; protected static final int default_activityDetectionRequiredProbability = 75;
protected static final boolean default_privacyLocationing = false; protected static final boolean default_privacyLocationing = false;
protected static final int default_startScreen = 0; 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_executeRulesAndProfilesWithSingleClick = false;
protected static final boolean default_displayNewsOnMainScreen = true; protected static final boolean default_displayNewsOnMainScreen = false;
protected static final boolean default_automaticUpdateCheck = false;
protected static final boolean default_lockSoundChanges = false; protected static final boolean default_lockSoundChanges = false;
protected static final long default_lastNewsPolltime = -1; protected static final long default_lastNewsPolltime = -1;
protected static final long default_lastUpdateCheck = -1;
@Override @Override
public boolean contains(String arg0) public boolean contains(String arg0)
@ -240,8 +255,10 @@ public class Settings implements SharedPreferences
privacyLocationing = prefs.getBoolean("privacyLocationing", default_privacyLocationing); privacyLocationing = prefs.getBoolean("privacyLocationing", default_privacyLocationing);
startScreen = Integer.parseInt(prefs.getString("startScreen", String.valueOf(default_startScreen))); 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); executeRulesAndProfilesWithSingleClick = prefs.getBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick);
automaticUpdateCheck = prefs.getBoolean("automaticUpdateCheck", default_automaticUpdateCheck);
displayNewsOnMainScreen = prefs.getBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen); displayNewsOnMainScreen = prefs.getBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen);
lockSoundChanges = prefs.getBoolean("lockSoundChanges", default_lockSoundChanges); lockSoundChanges = prefs.getBoolean("lockSoundChanges", default_lockSoundChanges);
@ -249,6 +266,17 @@ public class Settings implements SharedPreferences
noticeAndroid10WifiShown = prefs.getBoolean("noticeAndroid10WifiShown", false); noticeAndroid10WifiShown = prefs.getBoolean("noticeAndroid10WifiShown", false);
lastNewsPolltime = prefs.getLong("lastNewsPolltime", default_lastNewsPolltime); lastNewsPolltime = prefs.getLong("lastNewsPolltime", default_lastNewsPolltime);
lastUpdateCheck = prefs.getLong("lastUpdateCheck", default_lastUpdateCheck);
String whbdString = prefs.getString("whatHasBeenDone", "");
if(whbdString != null && whbdString.length() > 0)
{
whatHasBeenDone = new ArrayList<>();
for(String s : whbdString.split(";"))
{
whatHasBeenDone.add(s);
}
}
} }
catch(Exception e) catch(Exception e)
{ {
@ -260,6 +288,26 @@ public class Settings implements SharedPreferences
initializeSettings(context, false); initializeSettings(context, false);
} }
} }
public static void considerDone(String key)
{
if(whatHasBeenDone == null)
whatHasBeenDone = new ArrayList<>();
if(!whatHasBeenDone.contains(key))
whatHasBeenDone.add(key);
}
public static boolean hasBeenDone(String key)
{
if(whatHasBeenDone != null)
{
if(whatHasBeenDone.contains(key))
return true;
}
return false;
}
/**Makes sure a settings has a valid setting. If not it will assign a reasonable default setting to it. /**Makes sure a settings has a valid setting. If not it will assign a reasonable default setting to it.
* If force settings will be initialized even if the user has set something.**/ * If force settings will be initialized even if the user has set something.**/
@ -395,9 +443,15 @@ public class Settings implements SharedPreferences
if(!prefs.contains("startScreen") | force) if(!prefs.contains("startScreen") | force)
editor.putString("startScreen", String.valueOf(default_startScreen)); editor.putString("startScreen", String.valueOf(default_startScreen));
if(!prefs.contains("tabsPlacement") | force)
editor.putString("tabsPlacement", String.valueOf(default_tabsPlacement));
if(!prefs.contains("executeRulesAndProfilesWithSingleClick") | force) if(!prefs.contains("executeRulesAndProfilesWithSingleClick") | force)
editor.putBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick); editor.putBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick);
if(!prefs.contains("automaticUpdateCheck") | force)
editor.putBoolean("automaticUpdateCheck", default_automaticUpdateCheck);
if(!prefs.contains("displayNewsOnMainScreen") | force) if(!prefs.contains("displayNewsOnMainScreen") | force)
editor.putBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen); editor.putBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen);
@ -409,6 +463,12 @@ public class Settings implements SharedPreferences
if(!prefs.contains("lastNewsPolltime") | force) if(!prefs.contains("lastNewsPolltime") | force)
editor.putLong("lastNewsPolltime", default_lastNewsPolltime); editor.putLong("lastNewsPolltime", default_lastNewsPolltime);
if(!prefs.contains("lastUpdateCheck") | force)
editor.putLong("lastUpdateCheck", default_lastUpdateCheck);
if(!prefs.contains("whatHasBeenDone") | force)
editor.putString("whatHasBeenDone", "");
editor.commit(); editor.commit();
@ -470,7 +530,9 @@ public class Settings implements SharedPreferences
editor.putString("activityDetectionRequiredProbability", String.valueOf(activityDetectionRequiredProbability)); editor.putString("activityDetectionRequiredProbability", String.valueOf(activityDetectionRequiredProbability));
editor.putBoolean("privacyLocationing", privacyLocationing); editor.putBoolean("privacyLocationing", privacyLocationing);
editor.putString("startScreen", String.valueOf(startScreen)); editor.putString("startScreen", String.valueOf(startScreen));
editor.putString("tabsPlacement", String.valueOf(tabsPlacement));
editor.putBoolean("executeRulesAndProfilesWithSingleClick", executeRulesAndProfilesWithSingleClick); editor.putBoolean("executeRulesAndProfilesWithSingleClick", executeRulesAndProfilesWithSingleClick);
editor.putBoolean("automaticUpdateCheck", automaticUpdateCheck);
editor.putBoolean("displayNewsOnMainScreen", displayNewsOnMainScreen); editor.putBoolean("displayNewsOnMainScreen", displayNewsOnMainScreen);
editor.putBoolean("lockSoundChanges", lockSoundChanges); editor.putBoolean("lockSoundChanges", lockSoundChanges);
@ -478,6 +540,9 @@ public class Settings implements SharedPreferences
editor.putBoolean("noticeAndroid10WifiShown", noticeAndroid10WifiShown); editor.putBoolean("noticeAndroid10WifiShown", noticeAndroid10WifiShown);
editor.putLong("lastNewsPolltime", lastNewsPolltime); editor.putLong("lastNewsPolltime", lastNewsPolltime);
editor.putLong("lastUpdateCheck", lastUpdateCheck);
editor.putString("whatHasBeenDone", Miscellaneous.explode(";", whatHasBeenDone));
if(lastActivePoi == null) if(lastActivePoi == null)
editor.putString("lastActivePoi", "null"); editor.putString("lastActivePoi", "null");
@ -517,4 +582,4 @@ public class Settings implements SharedPreferences
return null; return null;
} }
} }

View File

@ -21,7 +21,7 @@ public class Trigger
*/ */
public enum Trigger_Enum { public enum Trigger_Enum {
pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, notification, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy
public String getFullName(Context context) public String getFullName(Context context)
{ {
@ -59,6 +59,8 @@ public class Trigger
return context.getResources().getString(R.string.bluetoothConnection); return context.getResources().getString(R.string.bluetoothConnection);
case headsetPlugged: case headsetPlugged:
return context.getResources().getString(R.string.triggerHeadsetPlugged); return context.getResources().getString(R.string.triggerHeadsetPlugged);
case notification:
return context.getResources().getString(R.string.notification);
default: default:
return "Unknown"; return "Unknown";
} }
@ -67,15 +69,25 @@ public class Trigger
}; };
private boolean triggerParameter; //if true->started event, if false->stopped private boolean triggerParameter; //if true->started event, if false->stopped
private String triggerParameter2;
public static final String triggerParameter2Split = "tp2split";
private Trigger_Enum triggerType = null; private Trigger_Enum triggerType = null;
private PointOfInterest pointOfInterest = null; private PointOfInterest pointOfInterest = null;
private TimeFrame timeFrame; 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 double speed; //km/h
private long noiseLevelDb; private long noiseLevelDb;
private String wifiName = ""; private String processName = null;
private String processName = null;
private int batteryLevel; private int batteryLevel;
private int phoneDirection = 0; // 0=any, 1=incoming, 2=outgoing private int phoneDirection = 0; // 0=any, 1=incoming, 2=outgoing
private String phoneNumber = null; private String phoneNumber = null;
@ -206,6 +218,16 @@ public class Trigger
this.triggerParameter = triggerParameter; this.triggerParameter = triggerParameter;
} }
public String getTriggerParameter2()
{
return triggerParameter2;
}
public void setTriggerParameter2(String triggerParameter2)
{
this.triggerParameter2 = triggerParameter2;
}
public TimeFrame getTimeFrame() public TimeFrame getTimeFrame()
{ {
return timeFrame; return timeFrame;
@ -216,7 +238,6 @@ public class Trigger
this.timeFrame = timeFrame; this.timeFrame = timeFrame;
} }
@RequiresApi(api = Build.VERSION_CODES.KITKAT) @RequiresApi(api = Build.VERSION_CODES.KITKAT)
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Override @Override
@ -292,10 +313,10 @@ public class Trigger
break; break;
case wifiConnection: case wifiConnection:
String wifiDisplayName = ""; String wifiDisplayName = "";
if(this.getWifiName().length() == 0) if(this.getTriggerParameter2().length() == 0)
wifiDisplayName += Miscellaneous.getAnyContext().getResources().getString(R.string.anyWifi); wifiDisplayName += Miscellaneous.getAnyContext().getResources().getString(R.string.anyWifi);
else else
wifiDisplayName += this.getWifiName(); wifiDisplayName += this.getTriggerParameter2();
if(getTriggerParameter()) if(getTriggerParameter())
returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.connectedToWifi), wifiDisplayName)); returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.connectedToWifi), wifiDisplayName));
@ -325,21 +346,45 @@ public class Trigger
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.roaming)); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.roaming));
break; break;
case phoneCall: case phoneCall:
if(getPhoneDirection() == 1) String[] elements = triggerParameter2.split(triggerParameter2Split);
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.incomingAdjective) + " ");
else if(getPhoneDirection() == 2)
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingAdjective) + " ");
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.phoneCall)); 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 else
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.with) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.anyNumber)); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.number) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.matching) + " " + elements[2]);
if(getTriggerParameter()) returnString.append(" ");
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.started));
else if(elements[0].equals(Trigger.triggerPhoneCallStateRinging))
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.stopped)); 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; break;
case nfcTag: case nfcTag:
// This type doesn't have an activate/deactivate equivalent // This type doesn't have an activate/deactivate equivalent
@ -440,6 +485,46 @@ public class Trigger
else else
returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.headsetDisconnected), type)); returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.headsetDisconnected), type));
break; break;
case notification:
if(this.getTriggerParameter2().contains(triggerParameter2Split))
{
String[] params = getTriggerParameter2().split(triggerParameter2Split);
String app = params[0];
String titleDir = params[1];
String title = params[2];
String textDir = params[3];
String text;
if (params.length >= 5)
text = params[4];
else
text = "";
StringBuilder triggerBuilder = new StringBuilder();
String appString;
if (app.equalsIgnoreCase("-1"))
appString = Miscellaneous.getAnyContext().getResources().getString(R.string.anyApp);
else
appString = "app " + app;
if(triggerParameter)
triggerBuilder.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.postsNotification), appString));
else
triggerBuilder.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.removedNotification), appString));
if (title.length() > 0)
triggerBuilder.append(", " + Miscellaneous.getAnyContext().getString(R.string.title) + " " + Trigger.getMatchString(titleDir) + " " + title);
if (text.length() > 0)
triggerBuilder.append(", " + Miscellaneous.getAnyContext().getString(R.string.text) + " " + Trigger.getMatchString(textDir) + " " + text);
returnString.append(triggerBuilder.toString());
}
else
{
setTriggerParameter2("-1" + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split);
}
break;
default: default:
returnString.append("error"); returnString.append("error");
break; break;
@ -447,7 +532,47 @@ public class Trigger
return returnString.toString(); return returnString.toString();
} }
public static final String directionEquals = "eq";
public static final String directionContains = "ct";
public static final String directionStartsWith = "sw";
public static final String directionEndsWith = "ew";
public static final String directionNotEquals = "ne";
public static String getMatchString(String direction)
{
switch(direction)
{
case directionEquals:
return Miscellaneous.getAnyContext().getString(R.string.directionStringEquals);
case directionContains:
return Miscellaneous.getAnyContext().getString(R.string.directionStringContains);
case directionStartsWith:
return Miscellaneous.getAnyContext().getString(R.string.directionStringStartsWith);
case directionEndsWith:
return Miscellaneous.getAnyContext().getString(R.string.directionStringEndsWith);
case directionNotEquals:
return Miscellaneous.getAnyContext().getString(R.string.directionStringNotEquals);
default:
return Miscellaneous.getAnyContext().getString(R.string.error);
}
}
public static String getMatchCode(String direction)
{
if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringEquals)))
return directionEquals;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringContains)))
return directionContains;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringStartsWith)))
return directionStartsWith;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringEndsWith)))
return directionEndsWith;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringNotEquals)))
return directionNotEquals;
else
return Miscellaneous.getAnyContext().getString(R.string.error);
}
public static String[] getTriggerTypesAsArray() public static String[] getTriggerTypesAsArray()
{ {
@ -478,15 +603,6 @@ public class Trigger
return (String[])triggerTypesList.toArray(new String[triggerTypesList.size()]); 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) public void setBluetoothEvent(String string)
{ {
this.bluetoothEvent = string; this.bluetoothEvent = string;

View File

@ -23,9 +23,12 @@ import java.security.GeneralSecurityException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import static com.jens.automation2.Trigger.triggerParameter2Split;
public class XmlFileInterface public class XmlFileInterface
{ {
public static File settingsFile = new File(Miscellaneous.getWriteableFolder() + "/Automation_settings.xml"); public static String settingsFileName = "Automation_settings.xml";
public static File settingsFile = new File(Miscellaneous.getWriteableFolder() + "/" + settingsFileName);
public static Context context; public static Context context;
protected static final String encryptionKey = "Y1vsP12L2S3NkTJbDOR4bQ6i02hsoo"; protected static final String encryptionKey = "Y1vsP12L2S3NkTJbDOR4bQ6i02hsoo";
@ -90,7 +93,7 @@ public class XmlFileInterface
{ {
//start a tag called "root" //start a tag called "root"
serializer.startTag(null, "PointOfInterest"); serializer.startTag(null, "PointOfInterest");
//i indent code just to have a view similar to xml-tree //i indent code just to have a view similar to xml-tree
serializer.startTag(null, "name"); serializer.startTag(null, "name");
serializer.text(PointOfInterest.getPointOfInterestCollection().get(i).getName()); serializer.text(PointOfInterest.getPointOfInterestCollection().get(i).getName());
@ -252,13 +255,13 @@ public class XmlFileInterface
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.noiseLevel) 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())); 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) 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) 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()); serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getProcessName());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.batteryLevel) 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())); 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) // 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())); // 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) else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.nfcTag)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getNfcTagId()); serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getNfcTagId());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.activityDetection) else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.activityDetection)
@ -270,6 +273,10 @@ public class XmlFileInterface
} }
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.headsetPlugged) else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.headsetPlugged)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getHeadphoneType())); 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, "TriggerParameter2");
serializer.endTag(null, "Trigger"); serializer.endTag(null, "Trigger");
} }
@ -344,17 +351,22 @@ public class XmlFileInterface
public static void readFile() throws FileNotFoundException public static void readFile() throws FileNotFoundException
{ {
if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, Miscellaneous.getAnyContext())) /*
{ Storage location has been moved to app-specific folder in Android/data
/* Hence this permission is not requested any more. If it is already granted we assume the files are on /sdcard or similar.
Don't have permission to access external storage. This is a show stopper as Migration to app-specific folder has yet to be implemented.
the configuration file is stored on external storage. */
*/ // if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, Miscellaneous.getAnyContext()))
Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4); // {
Toast.makeText(Miscellaneous.getAnyContext(), Miscellaneous.getAnyContext().getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show(); // /*
ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName); // Don't have permission to access external storage. This is a show stopper as
return; // the configuration file is stored on external storage.
} // */
// Miscellaneous.logEvent("e", "Permission", "Don't have permission to access external storage. Will request it now.", 4);
// Toast.makeText(Miscellaneous.getAnyContext(), Miscellaneous.getAnyContext().getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// ActivityPermissions.requestSpecificPermission(ActivityPermissions.writeExternalStoragePermissionName);
// return;
// }
/* /*
If we are here it may be that we just got permission to read storage. We need to check for the If we are here it may be that we just got permission to read storage. We need to check for the
@ -371,7 +383,7 @@ public class XmlFileInterface
} }
catch (XmlPullParserException e) catch (XmlPullParserException e)
{ {
e.printStackTrace(); Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
} }
catch(FileNotFoundException e) catch(FileNotFoundException e)
{ {
@ -386,12 +398,12 @@ public class XmlFileInterface
} }
catch(Exception ex) catch(Exception ex)
{ {
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
} }
} }
catch (IOException e) catch (IOException e)
{ {
e.printStackTrace(); Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
} }
catch(Exception e) catch(Exception e)
{ {
@ -526,11 +538,11 @@ public class XmlFileInterface
} }
catch (NumberFormatException e) catch (NumberFormatException e)
{ {
e.printStackTrace(); Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
} }
} }
else else
@ -743,13 +755,11 @@ public class XmlFileInterface
} }
catch (XmlPullParserException e) catch (XmlPullParserException e)
{ {
// TODO Auto-generated catch block Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
e.printStackTrace();
} }
catch (IOException e) catch (IOException e)
{ {
// TODO Auto-generated catch block Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
e.printStackTrace();
} }
} }
else if (name.equals("ActionCollection")) else if (name.equals("ActionCollection"))
@ -760,13 +770,11 @@ public class XmlFileInterface
} }
catch (XmlPullParserException e) catch (XmlPullParserException e)
{ {
// TODO Auto-generated catch block Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
e.printStackTrace();
} }
catch (IOException e) catch (IOException e)
{ {
// TODO Auto-generated catch block Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
e.printStackTrace();
} }
} }
else else
@ -857,38 +865,11 @@ public class XmlFileInterface
if (name.equals("TriggerEvent")) if (name.equals("TriggerEvent"))
{ {
String triggerEventString = readTag(parser, "TriggerEvent"); String triggerEventString = readTag(parser, "TriggerEvent");
if(triggerEventString.equals("pointOfInterest"))
newTrigger.setTriggerType(Trigger_Enum.pointOfInterest); if(triggerEventString.equals("process_started_stopped") | triggerEventString.equals("process_running"))
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"))
newTrigger.setTriggerType(Trigger_Enum.process_started_stopped); newTrigger.setTriggerType(Trigger_Enum.process_started_stopped);
else if(triggerEventString.equals("airplaneMode")) else
newTrigger.setTriggerType(Trigger_Enum.airplaneMode); newTrigger.setTriggerType(Trigger_Enum.valueOf(triggerEventString));
else if(triggerEventString.equals("roaming"))
newTrigger.setTriggerType(Trigger_Enum.roaming);
else if(triggerEventString.equals("phoneCall"))
newTrigger.setTriggerType(Trigger_Enum.phoneCall);
else if(triggerEventString.equals("nfcTag"))
newTrigger.setTriggerType(Trigger_Enum.nfcTag);
else if(triggerEventString.equals("activityDetection"))
newTrigger.setTriggerType(Trigger_Enum.activityDetection);
else if(triggerEventString.equals("bluetoothConnection"))
newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection);
else if(triggerEventString.equals("headsetPlugged"))
newTrigger.setTriggerType(Trigger_Enum.headsetPlugged);
} }
else if (name.equals("TriggerParameter1")) else if (name.equals("TriggerParameter1"))
{ {
@ -911,42 +892,91 @@ public class XmlFileInterface
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 2); Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 2);
Toast.makeText(context, "Error while writing file: " + Log.getStackTraceString(e), Toast.LENGTH_LONG).show(); Toast.makeText(context, "Error while writing file: " + Log.getStackTraceString(e), Toast.LENGTH_LONG).show();
} }
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.timeFrame) else if(newTrigger.getTriggerType() == Trigger_Enum.timeFrame)
{ {
newTrigger.setTimeFrame(new TimeFrame(triggerParameter2)); newTrigger.setTimeFrame(new TimeFrame(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.batteryLevel) else if(newTrigger.getTriggerType() == Trigger_Enum.batteryLevel)
{ {
newTrigger.setBatteryLevel(Integer.parseInt(triggerParameter2)); newTrigger.setBatteryLevel(Integer.parseInt(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.speed) else if(newTrigger.getTriggerType() == Trigger_Enum.speed)
{ {
newTrigger.setSpeed(Double.parseDouble(triggerParameter2)); newTrigger.setSpeed(Double.parseDouble(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.noiseLevel) else if(newTrigger.getTriggerType() == Trigger_Enum.noiseLevel)
{ {
newTrigger.setNoiseLevelDb(Long.parseLong(triggerParameter2)); newTrigger.setNoiseLevelDb(Long.parseLong(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.wifiConnection) 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) else if(newTrigger.getTriggerType() == Trigger_Enum.process_started_stopped)
{ {
newTrigger.setProcessName(triggerParameter2); newTrigger.setProcessName(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.phoneCall) else if(newTrigger.getTriggerType() == Trigger_Enum.phoneCall)
{ {
// 0/1/2,number String[] elements = triggerParameter2.split(",");
int direction = Integer.parseInt(triggerParameter2.substring(0, 1)); if(elements.length == 2) //old format
String number = triggerParameter2.substring(2); {
newTrigger.setPhoneDirection(direction); // 0/1/2,number
newTrigger.setPhoneNumber(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) else if(newTrigger.getTriggerType() == Trigger_Enum.nfcTag)
{ {
newTrigger.setNfcTagId(triggerParameter2); newTrigger.setNfcTagId(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.activityDetection) else if(newTrigger.getTriggerType() == Trigger_Enum.activityDetection)
{ {
@ -958,6 +988,7 @@ public class XmlFileInterface
{ {
newTrigger.setActivityDetectionType(0); newTrigger.setActivityDetectionType(0);
} }
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.bluetoothConnection) else if(newTrigger.getTriggerType() == Trigger_Enum.bluetoothConnection)
{ {
@ -967,6 +998,7 @@ public class XmlFileInterface
newTrigger.setBluetoothEvent(substrings[0]); newTrigger.setBluetoothEvent(substrings[0]);
newTrigger.setBluetoothDeviceAddress(substrings[1]); newTrigger.setBluetoothDeviceAddress(substrings[1]);
} }
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else if(newTrigger.getTriggerType() == Trigger_Enum.headsetPlugged) else if(newTrigger.getTriggerType() == Trigger_Enum.headsetPlugged)
{ {
@ -978,7 +1010,10 @@ public class XmlFileInterface
{ {
newTrigger.setHeadphoneType(-1); newTrigger.setHeadphoneType(-1);
} }
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else
newTrigger.setTriggerParameter2(triggerParameter2);
} }
else else
{ {
@ -1066,19 +1101,9 @@ public class XmlFileInterface
{ {
String actionNameString = readTag(parser, "ActionName"); 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 // *** deprecated
else if(actionNameString.equals("turnWifiOn")) //else
if(actionNameString.equals("turnWifiOn"))
newAction.setAction(Action_Enum.turnWifiOn); newAction.setAction(Action_Enum.turnWifiOn);
else if(actionNameString.equals("turnWifiOff")) else if(actionNameString.equals("turnWifiOff"))
newAction.setAction(Action_Enum.turnWifiOff); newAction.setAction(Action_Enum.turnWifiOff);
@ -1099,29 +1124,9 @@ public class XmlFileInterface
else if(actionNameString.equals("disableScreenRotation")) else if(actionNameString.equals("disableScreenRotation"))
newAction.setAction(Action_Enum.disableScreenRotation); newAction.setAction(Action_Enum.disableScreenRotation);
// *** deprecated // *** deprecated
else if(actionNameString.equals("triggerUrl")) else
newAction.setAction(Action_Enum.triggerUrl); newAction.setAction(Action_Enum.valueOf(actionNameString));
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 if (name.equals("ActionParameter1")) else if (name.equals("ActionParameter1"))
{ {
@ -1207,8 +1212,43 @@ public class XmlFileInterface
{ {
newAction.setParameter2(tag); 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 else
newAction.setParameter2(tag); newAction.setParameter2(tag);
} }

View File

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

View File

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

View File

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

View File

@ -144,8 +144,33 @@ public class LocationProvider
} }
else else
{ {
speedCalculation:
if (locationList.size() >= 2) 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); Miscellaneous.logEvent("i", "Speed", "Trying to calculate speed based on the last locations.", 4);
double currentSpeed; double currentSpeed;
@ -184,14 +209,6 @@ public class LocationProvider
} }
else else
Miscellaneous.logEvent("i", "Speed", "Last two locations are too far apart in terms of time. Cannot use them for speed calculation.", 4); 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 else
{ {
@ -489,18 +506,10 @@ public class LocationProvider
{ {
// time is up, no cell location updates since x minutes, start accelerometer // 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."; 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"); Location currentLocation = CellLocationChangedReceiver.getInstance().getLocation("coarse");
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false); 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.net.wifi.WifiManager;
import android.util.Log; import android.util.Log;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous; import com.jens.automation2.Miscellaneous;
import com.jens.automation2.PointOfInterest; import com.jens.automation2.PointOfInterest;
import com.jens.automation2.R; import com.jens.automation2.R;
@ -28,9 +29,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
protected static WifiBroadcastReceiver wifiBrInstance; protected static WifiBroadcastReceiver wifiBrInstance;
protected static IntentFilter wifiListenerIntentFilter; protected static IntentFilter wifiListenerIntentFilter;
protected static boolean wifiListenerActive=false; protected static boolean wifiListenerActive=false;
public static String getLastWifiSsid() public static String getLastWifiSsid()
{ {
return lastWifiSsid; return lastWifiSsid;
@ -103,7 +102,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
Miscellaneous.logEvent("i", "WifiReceiver", context.getResources().getString(R.string.poiHasNoWifiNotStoppingCellLocationListener), 2); 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 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(); String ssid = myWifiManager.getConnectionInfo().getSSID();
setLastWifiSsid(ssid); setLastWifiSsid(ssid);
lastConnectedState = true; lastConnectedState = true;
findRules(parentLocationProvider); findRules(AutomationService.getInstance());
} }
else if(!myWifi.isConnectedOrConnecting()) // really disconnected? because sometimes also fires on connect else if(!myWifi.isConnectedOrConnecting()) // really disconnected? because sometimes also fires on connect
{ {
@ -128,7 +127,7 @@ public class WifiBroadcastReceiver extends BroadcastReceiver
mayCellLocationChangedReceiverBeActivatedFromWifiPointOfWifi = true; mayCellLocationChangedReceiverBeActivatedFromWifiPointOfWifi = true;
CellLocationChangedReceiver.startCellLocationChangedReceiver(); CellLocationChangedReceiver.startCellLocationChangedReceiver();
lastConnectedState = false; lastConnectedState = false;
findRules(parentLocationProvider); findRules(AutomationService.getInstance());
} }
catch(Exception e) 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(); ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByWifiConnection();
for(Rule oneRule : ruleCandidates) for(Rule oneRule : ruleCandidates)
{ {
if(oneRule.applies(parentLocationProvider.parentService)) if(oneRule.applies(automationServiceInstance))
oneRule.activate(parentLocationProvider.parentService, false); 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); WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo(); WifiInfo wifiInfo = wifiManager.getConnectionInfo();
WifiBroadcastReceiver.setLastWifiSsid(wifiInfo.getSSID()); WifiBroadcastReceiver.setLastWifiSsid(wifiInfo.getSSID());
WifiBroadcastReceiver.findRules(automationServiceRef.getLocationProvider()); WifiBroadcastReceiver.findRules(automationServiceRef);
break; break;
case ConnectivityManager.TYPE_MOBILE: case ConnectivityManager.TYPE_MOBILE:
boolean isRoaming = isRoaming(context); 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. // 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); Miscellaneous.logEvent("i", "Connectivity", "Wifi deactivated while having been connected before.", 4);
WifiBroadcastReceiver.lastConnectedState = false; WifiBroadcastReceiver.lastConnectedState = false;
WifiBroadcastReceiver.findRules(automationServiceRef.getLocationProvider()); WifiBroadcastReceiver.findRules(automationServiceRef);
} }
} }
} }

View File

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

View File

@ -1,5 +1,7 @@
package com.jens.automation2.receivers; package com.jens.automation2.receivers;
import android.Manifest;
import android.app.Service;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -13,16 +15,18 @@ import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous; import com.jens.automation2.Miscellaneous;
import com.jens.automation2.R; import com.jens.automation2.R;
import com.jens.automation2.Rule; import com.jens.automation2.Rule;
import com.jens.automation2.Trigger;
import com.jens.automation2.Trigger.Trigger_Enum; import com.jens.automation2.Trigger.Trigger_Enum;
import java.util.ArrayList; import java.util.ArrayList;
public class PhoneStatusListener implements AutomationListenerInterface public class PhoneStatusListener implements AutomationListenerInterface
{ {
protected static int currentStateIncoming = -1; // protected static int currentStateIncoming = -1;
protected static int currentStateOutgoing = -1; // protected static int currentStateOutgoing = -1;
protected static String lastPhoneNumber=""; protected static String lastPhoneNumber="";
protected static int lastPhoneDirection = -1; //0=incoming, 1=outgoing protected static int lastPhoneDirection = -1; //0=incoming, 1=outgoing
protected static int currentState = -1;
protected static boolean incomingCallsReceiverActive = false; protected static boolean incomingCallsReceiverActive = false;
protected static boolean outgoingCallsReceiverActive = false; protected static boolean outgoingCallsReceiverActive = false;
@ -31,7 +35,6 @@ public class PhoneStatusListener implements AutomationListenerInterface
protected static IncomingCallsReceiver incomingCallsReceiverInstance; protected static IncomingCallsReceiver incomingCallsReceiverInstance;
protected static BroadcastReceiver outgoingCallsReceiverInstance; protected static BroadcastReceiver outgoingCallsReceiverInstance;
public static boolean isIncomingCallsReceiverActive() public static boolean isIncomingCallsReceiverActive()
{ {
return incomingCallsReceiverActive; return incomingCallsReceiverActive;
@ -58,7 +61,17 @@ public class PhoneStatusListener implements AutomationListenerInterface
{ {
return lastPhoneNumber; return lastPhoneNumber;
} }
public static void setCurrentState(int currentState)
{
PhoneStatusListener.currentState = currentState;
}
public static int getCurrentState()
{
return currentState;
}
public static class IncomingCallsReceiver extends PhoneStateListener public static class IncomingCallsReceiver extends PhoneStateListener
{ {
@Override @Override
@ -66,174 +79,126 @@ public class PhoneStatusListener implements AutomationListenerInterface
{ {
// Miscellaneous.logEvent("i", "Call state", "New call state: " + String.valueOf(state), 4); // 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); Unfortunately receivers for incoming and outgoing calls behave pretty differently:
switch(state) 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: // This status update is actually for an outgoing call
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_IDLE", 4); setCurrentState(state);
if(currentStateIncoming == TelephonyManager.CALL_STATE_OFFHOOK)
setCurrentStateIncoming(state); if(incomingNumber != null && incomingNumber.length() > 0) // check for null in case call comes in with suppressed number.
else if(currentStateOutgoing == TelephonyManager.CALL_STATE_OFFHOOK) setLastPhoneNumber(incomingNumber);
setCurrentStateOutgoing(state);
else switch(state)
currentStateIncoming = state; {
currentStateOutgoing = state; case TelephonyManager.CALL_STATE_IDLE:
break; Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_IDLE", 4);
case TelephonyManager.CALL_STATE_OFFHOOK: break;
Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_OFFHOOK", 4); case TelephonyManager.CALL_STATE_OFFHOOK:
if(currentStateIncoming == TelephonyManager.CALL_STATE_RINGING) Miscellaneous.logEvent("i", "Call state", "New call state: CALL_STATE_OFFHOOK", 4);
setCurrentStateIncoming(state); break;
else if(currentStateOutgoing == TelephonyManager.CALL_STATE_RINGING) case TelephonyManager.CALL_STATE_RINGING:
setCurrentStateOutgoing(state); Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingCallTo), incomingNumber), 4);
break; break;
case TelephonyManager.CALL_STATE_RINGING: }
String number = "unknown";
if(incomingNumber != null && incomingNumber.length() > 0) ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionOutgoing);
number = incomingNumber; for(int i=0; i<ruleCandidates.size(); i++)
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.incomingCallFrom), number), 4); {
AutomationService asInstance = AutomationService.getInstance();
setCurrentStateIncoming(state); if(asInstance != null)
break; 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 public static class OutgoingCallsReceiver extends BroadcastReceiver
{ {
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
setCurrentStateOutgoing(2); /*
setLastPhoneNumber(intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)); This receiver is ONLY triggered when outgoing calls ring, not when that call is established or ends.
Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingCallFrom), getLastPhoneNumber()), 4); */
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() public static boolean isInACall()
{ {
if(isInIncomingCall() | isInOutgoingCall()) return getCurrentState() != TelephonyManager.CALL_STATE_IDLE;
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;
} }
public static void setCurrentStateOutgoing(int state) /*
{ Future remark:
if(currentStateOutgoing != state) Apps that redirect outgoing calls should use the android.telecom.CallRedirectionService API.
{ Apps that perform call screening should use the android.telecom.CallScreeningService API.
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;
}
public static void startPhoneStatusListener(AutomationService automationService) public static void startPhoneStatusListener(AutomationService automationService)
{ {
@ -312,9 +277,9 @@ public class PhoneStatusListener implements AutomationListenerInterface
public static boolean haveAllPermission() public static boolean haveAllPermission()
{ {
return 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 @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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.os.Handler;
import android.widget.Toast; 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 * Base application class to extend from, solving some issues with
* toasts and AsyncTasks you are likely to run into * toasts and AsyncTasks you are likely to run into
*/ */
@SuppressWarnings("WeakerAccess")
public class Application extends android.app.Application { public class Application extends android.app.Application {
/** /**
* Shows a toast message * Shows a toast message
* *
* @param context Any context belonging to this application * @param context Any context belonging to this application
* @param message The message to show * @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, // this is a static method so it is easier to call,
// as the context checking and casting is done for you // 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 Context c = context;
final String m = message; final String m = message;
((Application)context).runInApplicationThread(new Runnable() { ((Application) context).runInApplicationThread(new Runnable() {
@Override @Override
public void run() { public void run() {
Toast.makeText(c, m, Toast.LENGTH_LONG).show(); 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 * Run a runnable in the main application thread
* *
* @param r Runnable to run * @param r Runnable to run
*/ */
public void runInApplicationThread(Runnable r) { @AnyThread
public void runInApplicationThread(@NonNull Runnable r) {
mApplicationHandler.post(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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.os.Looper;
import android.util.Log; 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; import com.jens.automation2.BuildConfig;
/** /**
* Utility class for logging and debug features that (by default) does nothing when not in debug mode * 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 { public class Debug {
// ----- DEBUGGING ----- // ----- DEBUGGING -----
@ -32,23 +39,23 @@ public class Debug {
/** /**
* <p>Enable or disable debug mode</p> * <p>Enable or disable debug mode</p>
* *
* <p>By default, debug mode is enabled for development * <p>By default, debug mode is enabled for development
* builds and disabled for exported APKs - see * builds and disabled for exported APKs - see
* BuildConfig.DEBUG</p> * BuildConfig.DEBUG</p>
* *
* @param enable Enable debug mode ? * @param enable Enable debug mode ?
*/ */
public static void setDebug(boolean enable) { public static void setDebug(boolean enable) {
debug = enable; debug = enable;
} }
/** /**
* <p>Is debug mode enabled ?</p> * <p>Is debug mode enabled ?</p>
* *
* @return Debug mode enabled * @return Debug mode enabled
*/ */
public static boolean getDebug() { public static boolean getDebug() {
return debug; return debug;
} }
@ -63,25 +70,27 @@ public class Debug {
public static final int LOG_GENERAL = 0x0001; public static final int LOG_GENERAL = 0x0001;
public static final int LOG_COMMAND = 0x0002; public static final int LOG_COMMAND = 0x0002;
public static final int LOG_OUTPUT = 0x0004; 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_NONE = 0x0000;
public static final int LOG_ALL = 0xFFFF; public static final int LOG_ALL = 0xFFFF;
private static int logTypes = LOG_ALL; private static int logTypes = LOG_ALL;
@Nullable
private static OnLogListener logListener = null; private static OnLogListener logListener = null;
/** /**
* <p>Log a message (internal)</p> * <p>Log a message (internal)</p>
* *
* <p>Current debug and enabled logtypes decide what gets logged - * <p>Current debug and enabled logtypes decide what gets logged -
* even if a custom callback is registered</p> * even if a custom callback is registered</p>
* *
* @param type Type of message to log * @param type Type of message to log
* @param typeIndicator String indicator for message type * @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 (debug && ((logTypes & type) == type)) {
if (logListener != null) { if (logListener != null) {
logListener.onLog(type, typeIndicator, message); logListener.onLog(type, typeIndicator, message);
@ -89,52 +98,61 @@ public class Debug {
Log.d(TAG, "[" + TAG + "][" + typeIndicator + "]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message); Log.d(TAG, "[" + TAG + "][" + typeIndicator + "]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message);
} }
} }
} }
/** /**
* <p>Log a "general" message</p> * <p>Log a "general" message</p>
* *
* <p>These messages are infrequent and mostly occur at startup/shutdown or on error</p> * <p>These messages are infrequent and mostly occur at startup/shutdown or on error</p>
* *
* @param message The message to log * @param message The message to log
*/ */
public static void log(String message) { public static void log(@NonNull String message) {
logCommon(LOG_GENERAL, "G", message); logCommon(LOG_GENERAL, "G", message);
} }
/** /**
* <p>Log a "per-command" message</p> * <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> * <p>This could produce a lot of output if the client runs many commands in the session</p>
* *
* @param message The message to log * @param message The message to log
*/ */
public static void logCommand(String message) { public static void logCommand(@NonNull String message) {
logCommon(LOG_COMMAND, "C", message); logCommon(LOG_COMMAND, "C", message);
} }
/** /**
* <p>Log a line of stdout/stderr output</p> * <p>Log a line of stdout/stderr output</p>
* *
* <p>This could produce a lot of output if the shell commands are noisy</p> * <p>This could produce a lot of output if the shell commands are noisy</p>
* *
* @param message The message to log * @param message The message to log
*/ */
public static void logOutput(String message) { public static void logOutput(@NonNull String message) {
logCommon(LOG_OUTPUT, "O", 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>Enable or disable logging specific types of message</p>
* *
* <p>You may | (or) LOG_* constants together. Note that * <p>You may | (or) LOG_* constants together. Note that
* debug mode must also be enabled for actual logging to * debug mode must also be enabled for actual logging to
* occur.</p> * occur.</p>
* *
* @param type LOG_* constants * @param type LOG_* constants
* @param enable Enable or disable * @param enable Enable or disable
*/ */
public static void setLogTypeEnabled(int type, boolean enable) { public static void setLogTypeEnabled(int type, boolean enable) {
if (enable) { if (enable) {
logTypes |= type; logTypes |= type;
} else { } else {
@ -144,26 +162,28 @@ public class Debug {
/** /**
* <p>Is logging for specific types of messages enabled ?</p> * <p>Is logging for specific types of messages enabled ?</p>
* *
* <p>You may | (or) LOG_* constants together, to learn if * <p>You may | (or) LOG_* constants together, to learn if
* <b>all</b> passed message types are enabled for logging. Note * <b>all</b> passed message types are enabled for logging. Note
* that debug mode must also be enabled for actual logging * that debug mode must also be enabled for actual logging
* to occur.</p> * to occur.</p>
* *
* @param type LOG_* constants * @param type LOG_* constants
* @return enabled?
*/ */
public static boolean getLogTypeEnabled(int type) { public static boolean getLogTypeEnabled(int type) {
return ((logTypes & type) == type); return ((logTypes & type) == type);
} }
/** /**
* <p>Is logging for specific types of messages enabled ?</p> * <p>Is logging for specific types of messages enabled ?</p>
* *
* <p>You may | (or) LOG_* constants together, to learn if * <p>You may | (or) LOG_* constants together, to learn if
* <b>all</b> message types are enabled for logging. Takes * <b>all</b> message types are enabled for logging. Takes
* debug mode into account for the result.</p> * debug mode into account for the result.</p>
* *
* @param type LOG_* constants * @param type LOG_* constants
* @return enabled and in debug mode?
*/ */
public static boolean getLogTypeEnabledEffective(int type) { public static boolean getLogTypeEnabledEffective(int type) {
return getDebug() && getLogTypeEnabled(type); return getDebug() && getLogTypeEnabled(type);
@ -171,22 +191,23 @@ public class Debug {
/** /**
* <p>Register a custom log handler</p> * <p>Register a custom log handler</p>
* *
* <p>Replaces the log method (write to logcat) with your own * <p>Replaces the log method (write to logcat) with your own
* handler. Whether your handler gets called is still dependent * handler. Whether your handler gets called is still dependent
* on debug mode and message types being enabled for logging.</p> * on debug mode and message types being enabled for logging.</p>
* *
* @param onLogListener Custom log listener or NULL to revert to default * @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; logListener = onLogListener;
} }
/** /**
* <p>Get the currently registered custom log handler</p> * <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() { public static OnLogListener getOnLogListener() {
return logListener; return logListener;
} }
@ -197,10 +218,10 @@ public class Debug {
/** /**
* <p>Enable or disable sanity checks</p> * <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> * from the main thread.</p>
* *
* @param enable Enable or disable * @param enable Enable or disable
*/ */
public static void setSanityChecksEnabled(boolean enable) { public static void setSanityChecksEnabled(boolean enable) {
@ -209,10 +230,10 @@ public class Debug {
/** /**
* <p>Are sanity checks enabled ?</p> * <p>Are sanity checks enabled ?</p>
* *
* <p>Note that debug mode must also be enabled for actual * <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 * @return True if enabled
*/ */
public static boolean getSanityChecksEnabled() { public static boolean getSanityChecksEnabled() {
@ -221,9 +242,9 @@ public class Debug {
/** /**
* <p>Are sanity checks enabled ?</p> * <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 * @return True if enabled
*/ */
public static boolean getSanityChecksEnabledEffective() { public static boolean getSanityChecksEnabledEffective() {
@ -232,11 +253,11 @@ public class Debug {
/** /**
* <p>Are we running on the main thread ?</p> * <p>Are we running on the main thread ?</p>
* *
* @return Running on main thread ? * @return Running on main thread ?
*/ */
public static boolean onMainThread() { 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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". * window possibly obscuring SuperSU dialogs".
* </p> * </p>
*/ */
@SuppressWarnings({"unused"})
public abstract class HideOverlaysReceiver extends BroadcastReceiver { public abstract class HideOverlaysReceiver extends BroadcastReceiver {
public static final String ACTION_HIDE_OVERLAYS = "eu.chainfire.supersu.action.HIDE_OVERLAYS"; public static final String ACTION_HIDE_OVERLAYS = "eu.chainfire.supersu.action.HIDE_OVERLAYS";
public static final String CATEGORY_HIDE_OVERLAYS = Intent.CATEGORY_INFO; public static final String CATEGORY_HIDE_OVERLAYS = Intent.CATEGORY_INFO;
@ -45,15 +46,17 @@ public abstract class HideOverlaysReceiver extends BroadcastReceiver {
@Override @Override
public final void onReceive(Context context, Intent intent) { public final void onReceive(Context context, Intent intent) {
if (intent.hasExtra(EXTRA_HIDE_OVERLAYS)) { 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 * Called when overlays <em>should</em> be hidden or <em>may</em> be shown
* again. * again.
* *
* @param context App context
* @param intent Received intent
* @param hide Should overlays be hidden? * @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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.List; 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 * 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 * Line callback interface
*/ */
public interface OnLineListener { public interface OnLineListener {
/** /**
* <p>Line callback</p> * <p>Line callback</p>
* *
* <p>This callback should process the line as quickly as possible. * <p>This callback should process the line as quickly as possible.
* Delays in this callback may pause the native process or even * Delays in this callback may pause the native process or even
* result in a deadlock</p> * result in a deadlock</p>
* *
* @param line String that was gobbled * @param line String that was gobbled
*/ */
void onLine(String line); void onLine(String line);
} }
private String shell = null; /**
private BufferedReader reader = null; * Stream closed callback interface
private List<String> writer = null; */
private OnLineListener listener = null; 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>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 * possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p> * returning (as the buffer is full, pausing the native process)</p>
* *
* @param shell Name of the shell * @param shell Name of the shell
* @param inputStream InputStream to read from * @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.shell = shell;
this.inputStream = inputStream;
reader = new BufferedReader(new InputStreamReader(inputStream)); reader = new BufferedReader(new InputStreamReader(inputStream));
writer = outputList; writer = outputList;
lineListener = null;
streamClosedListener = null;
} }
/** /**
* <p>StreamGobbler constructor</p> * <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 * possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p> * returning (as the buffer is full, pausing the native process)</p>
* *
* @param shell Name of the shell * @param shell Name of the shell
* @param inputStream InputStream to read from * @param inputStream InputStream to read from
* @param onLineListener OnLineListener callback * @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.shell = shell;
this.inputStream = inputStream;
reader = new BufferedReader(new InputStreamReader(inputStream)); reader = new BufferedReader(new InputStreamReader(inputStream));
listener = onLineListener; lineListener = onLineListener;
streamClosedListener = onStreamClosedListener;
writer = null;
} }
@Override @Override
public void run() { public void run() {
// keep reading the InputStream until it ends (or an error occurs) // keep reading the InputStream until it ends (or an error occurs)
// optionally pausing when a command is executed that consumes the InputStream itself
try { try {
String line; String line;
while ((line = reader.readLine()) != null) { 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 (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) { } catch (IOException e) {
// reader probably closed, expected exit condition // reader probably closed, expected exit condition
if (streamClosedListener != null) {
calledOnClose = true;
streamClosedListener.onStreamClosed();
}
} }
// make sure our stream is closed and resources will be freed // make sure our stream is closed and resources will be freed
@ -101,5 +162,96 @@ public class StreamGobbler extends Thread {
} catch (IOException e) { } catch (IOException e) {
// read already closed // read already closed
} }
if (!calledOnClose) {
if (streamClosedListener != null) {
calledOnClose = true;
streamClosedListener.onStreamClosed();
}
}
}
/**
* <p>Resume consuming the input from the stream</p>
*/
@AnyThread
public void resumeGobbling() {
if (!active) {
synchronized (this) {
active = true;
this.notifyAll();
}
}
}
/**
* <p>Suspend gobbling, so other code may read from the InputStream instead</p>
*
* <p>This should <i>only</i> be called from the OnLineListener callback!</p>
*/
@AnyThread
public void suspendGobbling() {
synchronized (this) {
active = false;
this.notifyAll();
}
}
/**
* <p>Wait for gobbling to be suspended</p>
*
* <p>Obviously this cannot be called from the same thread as {@link #suspendGobbling()}</p>
*/
@WorkerThread
public void waitForSuspend() {
synchronized (this) {
while (active) {
try {
this.wait(32);
} catch (InterruptedException e) {
// no action
}
}
}
}
/**
* <p>Is gobbling suspended ?</p>
*
* @return is gobbling suspended?
*/
@AnyThread
public boolean isSuspended() {
synchronized (this) {
return !active;
}
}
/**
* <p>Get current source InputStream</p>
*
* @return source InputStream
*/
@NonNull
@AnyThread
public InputStream getInputStream() {
return inputStream;
}
/**
* <p>Get current OnLineListener</p>
*
* @return OnLineListener
*/
@Nullable
@AnyThread
public OnLineListener getOnLineListener() {
return lineListener;
}
void conditionalJoin() throws InterruptedException {
if (calledOnClose) return; // deadlock from callback, we're inside exit procedure
if (Thread.currentThread() == this) return; // can't join self
join();
} }
} }

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

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

View File

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

View File

@ -0,0 +1,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

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

View File

@ -2,7 +2,7 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_margin="10dp" > android:layout_margin="@dimen/default_margin" >
<LinearLayout <LinearLayout
android:layout_width="match_parent" 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" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_margin="10dp" > android:layout_margin="@dimen/default_margin" >
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" > 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 <TextView
android:id="@+id/tvRuleTitle" android:id="@+id/tvRuleTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/urlToTrigger" /> android:text="@string/urlToTrigger" />
<EditText <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_height="match_parent"
android:layout_margin="@dimen/default_margin"> 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 <CheckBox
android:id="@+id/chkAutoBrightness" android:id="@+id/chkAutoBrightness"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" > android:layout_height="match_parent"
android:layout_margin="@dimen/default_margin">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -9,7 +10,6 @@
android:orientation="vertical" > android:orientation="vertical" >
<LinearLayout <LinearLayout
android:layout_marginTop="10dp"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -92,7 +92,7 @@
<Button <Button
android:id="@+id/bSaveBluetoothTrigger" android:id="@+id/bSaveBluetoothTrigger"
android:layout_marginTop="10dp" android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/save" /> android:text="@string/save" />

View File

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

View File

@ -0,0 +1,150 @@
<?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:text="@string/notification"
android:textSize="25dp"
android:layout_marginBottom="@dimen/default_margin" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/notificationTriggerExplanation" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1"
android:stretchColumns="1">
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/direction"/>
<CheckBox
android:id="@+id/chkNotificationDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notificationAppears"
android:checked="true"/>
</TableRow>
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/application" />
<LinearLayout
android:orientation="vertical"
android:layout_marginHorizontal="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/etActivityOrActionPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/anyApp"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/bSelectApp"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectApplication" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/title" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/spinnerTitleDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/etNotificationTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/text" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/spinnerTextDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/etNotificationText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
</TableRow>>
</TableLayout>
<Button
android:id="@+id/bSaveTriggerNotification"
android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</LinearLayout>
</ScrollView>

View File

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

@ -31,7 +31,7 @@
<CheckBoxPreference <CheckBoxPreference
android:key="writeLogFile" android:key="writeLogFile"
android:summary="@string/onOff" android:summary="@string/onOff"
android:title="@string/writeLogFileToSd" /> android:title="@string/writeLogFile" />
<EditTextPreference <EditTextPreference
android:key="logLevel" android:key="logLevel"
@ -52,10 +52,23 @@
android:entries="@array/startScreenOptions" android:entries="@array/startScreenOptions"
android:entryValues="@array/startScreenOptionsValues" /> 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 <CheckBoxPreference
android:key="executeRulesAndProfilesWithSingleClick" android:key="executeRulesAndProfilesWithSingleClick"
android:title="@string/executeRulesAndProfilesWithSingleClickTitle" /> android:title="@string/executeRulesAndProfilesWithSingleClickTitle" />
<CheckBoxPreference
android:key="automaticUpdateCheck"
android:enabled="false"
android:title="@string/automaticUpdateCheck"
android:summary="@string/automaticUpdateCheckSummary"/>
<CheckBoxPreference <CheckBoxPreference
android:key="displayNewsOnMainScreen" android:key="displayNewsOnMainScreen"
android:title="@string/displayNewsOnMainScreen" android:title="@string/displayNewsOnMainScreen"

View File

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

View File

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

View File

@ -27,7 +27,7 @@
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/tvMainScreenNote1" android:id="@+id/tvMainScreenNotePermissions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
@ -40,7 +40,7 @@
android:textAppearance="?android:attr/textAppearanceMedium" /> android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView <TextView
android:id="@+id/tvMainScreenNote2" android:id="@+id/tvMainScreenNoteFeaturesFromOtherFlavor"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
@ -53,7 +53,20 @@
android:textAppearance="?android:attr/textAppearanceMedium" /> android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView <TextView
android:id="@+id/tvMainScreenNote3" android:id="@+id/tvMainScreenNoteLocationImpossibleBlameGoogle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scrollHorizontally="false"
android:textColor="@color/importantMessage"
android:singleLine="false"
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvMainScreenNoteNews"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
@ -289,70 +302,36 @@
</TableRow> </TableRow>
</TableLayout> </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 <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="vertical"
android:layout_margin="10dp" android:layout_margin="10dp"
android:layout_marginTop="30dp" android:layout_marginTop="30dp"
android:gravity="center_horizontal" > 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 <Button
android:id="@+id/bSettings" android:id="@+id/bSettings"
android:layout_width="wrap_content" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top" android:layout_gravity="top"
android:layout_weight="1" android:text="@string/settings" />
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" />
</LinearLayout> </LinearLayout>

View File

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

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

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