Compare commits

...

234 Commits

Author SHA1 Message Date
2a067507ae new release 1.7.2 2022-01-10 10:33:49 +01:00
1c8eec735d code cleanups 2022-01-09 21:43:49 +01:00
53f46c10da translations 2022-01-09 14:55:52 +01:00
c5f04afe85 translations 2022-01-09 14:01:38 +01:00
4d7fa711f9 profile trigger 2022-01-09 13:51:38 +01:00
4bea2113fa profile trigger 2022-01-09 13:48:12 +01:00
890260b8eb startApp 2022-01-08 19:00:06 +01:00
230521149f startApp 2022-01-08 14:46:01 +01:00
9a50da550a bugs and translations 2022-01-05 23:27:27 +01:00
941bb3e1af profile trigger 2022-01-05 18:06:26 +01:00
5653a9c70e profile trigger 2022-01-04 17:51:27 +01:00
8c6331237d Cosmetics 2022-01-04 16:14:13 +01:00
1bbf04d548 Exclusion from battery optimization 2022-01-03 23:24:34 +01:00
a2d93d27cb Cosmetics 2022-01-03 19:50:57 +01:00
0463e0aa19 Silent mode bug 2021-12-27 13:58:43 +01:00
41cf907be3 Merge remote-tracking branch 'origin/development' 2021-12-24 01:50:44 +01:00
acae282a0d New changelog 2021-12-24 01:50:33 +01:00
ebfceee69f Translations 2021-12-24 01:38:42 +01:00
69283c5fea Translations 2021-12-21 20:02:13 +01:00
fff0bde9d8 Merge remote-tracking branch 'origin/development' into development 2021-12-20 19:58:56 +01:00
f9c76ba951 Rework 2021-12-20 19:58:43 +01:00
d1263b46b6 Refactoring 2021-12-19 22:31:05 +01:00
64801d8ff2 Rework 2021-12-19 17:12:24 +01:00
3d8257aeec Rework 2021-12-19 14:47:46 +01:00
67b2a81647 Rework 2021-12-19 12:58:33 +01:00
3e9590d7d2 Rework 2021-12-18 13:29:47 +01:00
21b8c6c7ec Rework 2021-12-18 02:43:04 +01:00
dc8cc14d20 Rework 2021-12-13 20:03:26 +01:00
4fc1f8a2a9 Rework 2021-12-13 20:03:00 +01:00
d7e1cd44e8 Rework 2021-12-12 20:03:53 +01:00
c1139e1cb8 Rework 2021-12-11 14:15:05 +01:00
03c8a1ff60 Rework 2021-12-11 02:04:45 +01:00
20acd563e7 Refactoring 2021-12-09 22:54:34 +01:00
5ae193847e Merge remote-tracking branch 'origin/development' into development
# Conflicts:
#	app/src/main/java/com/jens/automation2/Actions.java
2021-12-09 18:05:58 +01:00
391479b164 Rework 2021-12-09 18:03:00 +01:00
fa578b175d Refactoring 2021-12-08 23:08:05 +01:00
0008642044 Rework 2021-12-08 19:57:27 +01:00
359dd545c7 Merge remote-tracking branch 'origin/transfer_applies_to_trigger' into development 2021-12-08 17:22:33 +01:00
89ac69fd4b Rework 2021-12-08 17:22:18 +01:00
b88801500f Refactoring 2021-12-07 23:10:37 +01:00
128116025f Rework 2021-12-07 16:45:05 +01:00
5d3e89595f description 2021-12-05 22:28:30 +01:00
8e5ad15c34 Rework 2021-12-05 17:24:47 +01:00
8b29dd0985 Rework 2021-12-05 14:51:00 +01:00
c34ec83425 Rework 2021-12-04 13:24:43 +01:00
cdf1a8baa8 Rework 2021-12-04 02:39:37 +01:00
d28ee8d00d Fixed infinite loop 2021-12-03 22:30:34 +01:00
2bb6f81596 Position trigger 2021-11-30 18:25:51 +01:00
6f0dbc9555 Position trigger 2021-11-29 20:14:09 +01:00
a9bd7b9561 Position trigger 2021-11-28 20:01:58 +01:00
81d6ab7b5f Position trigger 2021-11-27 20:22:13 +01:00
034c76fe30 Translations and integrated BT tethering 2021-11-27 02:03:51 +01:00
15637e914d Translations and integrated BT tethering 2021-11-27 01:52:57 +01:00
cd6ed7543c Translations and integrated BT tethering 2021-11-26 19:26:42 +01:00
f991325566 Merge remote-tracking branch 'origin/BT_tethering' into development 2021-11-26 18:56:28 +01:00
b744e76b07 Negative location coordinates allowed 2021-11-23 13:27:09 +01:00
06d63826e6 Donate button 2021-11-20 16:14:21 +01:00
fb87d5e42d BT tethering 2021-11-17 21:46:56 +01:00
17109b12d4 BT tethering 2021-11-15 20:28:38 +01:00
92ca6d6cb4 BT tethering 2021-11-13 14:48:56 +01:00
5fdc68e396 Merge remote-tracking branch 'origin/reoccuring_time_trigger' into development 2021-11-13 12:31:27 +01:00
ab0f2d88b4 Reoccuring time trigger 2021-11-13 02:32:39 +01:00
06a6651fae Reoccuring time trigger 2021-11-13 02:17:36 +01:00
473c464bf7 BT tethering 2021-11-13 01:00:36 +01:00
a5b9ced9ba line break 2021-11-11 10:28:46 +01:00
9cea3f4285 reocc time 2021-11-10 21:47:09 +01:00
0438a58f3e translation 2021-11-09 11:10:06 +01:00
97f32bd012 Reoccuring time 2021-11-08 20:13:11 +01:00
6588443459 reoccuring time 2021-11-07 17:29:00 +01:00
604ab0eb43 miscellaneous 2021-11-07 15:38:42 +01:00
31c4f6c1d1 Fixed crash of edit wifi trigger 2021-11-07 02:09:09 +01:00
0c646b55fc Fixed infinite loop 2021-11-05 22:47:48 +01:00
88cdc366c5 Changelogs 2021-11-04 18:01:30 +01:00
2bd94e8a3d Changelogs 2021-11-03 15:30:49 +01:00
074f75ed20 permission for play sound 2021-11-03 15:25:21 +01:00
d988e1f43d permission for play sound 2021-11-03 15:15:26 +01:00
23502f52bb permission for play sound 2021-11-03 15:15:06 +01:00
9a6083247f permission for play sound 2021-10-31 13:27:45 +01:00
ba2a340bdf permission for play sound 2021-10-28 17:58:55 +02:00
9e2f7c16f6 vibrateWhenRinging 2021-10-09 02:19:34 +02:00
d042b3f35a Translations 2021-10-04 20:18:09 +02:00
220d2d316e Fix in data switch 2021-10-03 21:38:33 +02:00
4aa095e801 Fix in data switch 2021-10-03 21:20:47 +02:00
b5bd332ff5 data with root 2021-10-03 15:09:07 +02:00
969937f8a0 vibrate for calls 2021-09-27 20:12:40 +02:00
e3598cc475 DND 2021-09-26 19:54:17 +02:00
e63d97be0c DND 2021-09-26 13:58:48 +02:00
e60fb1535a bugspray 2021-09-25 02:03:44 +02:00
8563234db3 bugspray 2021-09-24 19:15:41 +02:00
448942e4e8 bugspray 2021-09-20 19:58:11 +02:00
dcdb770d9f bugspray 2021-09-15 21:58:18 +02:00
b5040cedb3 var replacement 2021-09-14 17:56:45 +02:00
423839fa43 Sound settings 2021-09-12 20:05:12 +02:00
a6edab75ce Location without Cell Radio 2021-09-07 17:30:45 +02:00
cb430b957f fixes 2021-09-07 10:01:30 +02:00
b6a0f6dd91 fixes 2021-09-02 17:56:17 +02:00
bc32cbc179 encoding related typos 2021-09-02 10:48:50 +02:00
d9cc604bdd gradle update 2021-09-01 21:28:36 +02:00
db21011b7f Notification fixed 2021-08-31 23:13:59 +02:00
dc35c8b7fb stacked notification almost fixed 2021-08-31 18:53:26 +02:00
5a7cbfcdc9 Enabled http cleartext traffic 2021-08-21 11:48:04 +02:00
913a37a320 disallow http again because wouldn't compile 2021-08-20 13:46:04 +02:00
14655fe55d allow http 2021-08-20 13:25:36 +02:00
cfc145c6c4 allow http 2021-08-18 10:00:41 +02:00
325bff305c Wifi fix 2021-08-14 14:11:19 +02:00
4371fb56f7 Merge remote-tracking branch 'origin/master' into development 2021-08-14 01:55:50 +02:00
6593f6c923 Compile fix 2021-08-14 01:53:48 +02:00
7fbac92360 New release 2021-07-29 14:19:20 +02:00
7415830dd7 New release 2021-07-29 14:14:20 +02:00
dce68d79bd New release 2021-07-29 14:05:57 +02:00
397dadd8c7 Translation 2021-07-28 11:49:13 +02:00
c51c46707b Translation 2021-07-21 13:06:42 +02:00
d699285b5a Translation 2021-07-16 18:33:25 +02:00
cf3db22ffb Translation 2021-07-08 22:41:58 +02:00
f53abe2b23 TabLayout 2021-07-08 17:57:33 +02:00
ba2f96713d TabLayout 2021-07-07 21:01:43 +02:00
21d9351b2b Translations 2021-07-07 12:52:26 +02:00
a8bfc6f7ec Vibrate pattern 2021-07-04 15:53:24 +02:00
71d9791603 Vibrate pattern 2021-07-04 02:27:07 +02:00
d71177f3da Vibrate pattern 2021-07-04 02:14:26 +02:00
eef6c3234a Speed calculation 2021-07-03 02:42:24 +02:00
192142c76b Translation 2021-07-02 10:46:25 +02:00
9a2a0aa6a4 Encryption 2021-06-29 23:10:51 +02:00
c18a880ad7 Logging 2021-06-28 23:28:46 +02:00
a845ea7c63 v1.6.35 2021-06-24 22:38:12 +02:00
2d9695344b Permission 2021-06-23 22:01:40 +02:00
24d05e6d87 Location 2021-06-23 20:20:38 +02:00
07b0513eae Permission 2021-06-20 23:45:02 +02:00
e445b787a9 Fixed cache problem after rule clone 2021-06-20 22:24:17 +02:00
82156059fa Translations 2021-06-16 23:12:00 +02:00
b976ff95b6 Translations 2021-06-16 21:45:29 +02:00
a4b2745b7b Layout changes 2021-06-13 13:53:14 +02:00
844be6a4a7 Manage POI 2021-06-12 18:56:03 +02:00
49a48fc371 Manage POI 2021-06-12 14:02:10 +02:00
722750b724 Translations. 2021-06-12 02:27:51 +02:00
ab51eb3655 POI management changes 2021-06-10 23:22:38 +02:00
7b88e7a612 Automatic update check for APK version. 2021-06-09 22:41:47 +02:00
bb2f5c9842 Translations. 2021-06-09 19:59:31 +02:00
883a7e8341 Translations. 2021-06-09 17:02:27 +02:00
bfc0e3ac4f Automatic update check for APK version. 2021-06-08 23:14:09 +02:00
23ded3a851 Minor corrections. 2021-06-08 20:04:02 +02:00
6d363fd02d Translations 2021-06-07 22:52:38 +02:00
3c0f889db7 Minor corrections. 2021-06-07 20:02:42 +02:00
d018c27f0e Minor corrections. 2021-06-05 12:11:05 +02:00
7e36f0f32e New release 2021-06-02 22:36:39 +02:00
666129de16 New release 2021-06-02 22:29:07 +02:00
becdbd6546 Hunting crash in Fdroid version 2021-06-02 17:12:33 +02:00
d292988737 Hunting crash in Fdroid version 2021-06-01 20:07:15 +02:00
21ee06e9b1 Wifi tethering without root above Oreo works again. 2021-05-30 20:03:30 +02:00
f22e4854ee Merge branch 'WifiRouter' into development
# Conflicts:
#	app/src/main/res/values-es/strings.xml
2021-05-30 12:28:59 +02:00
7182698b8a Wifi tethering without root above Oreo works again. 2021-05-30 12:27:27 +02:00
7fd8d1cfd0 Translation 2021-05-26 19:55:21 +02:00
943928089b Translation 2021-05-22 23:12:40 +02:00
f70e45701f Wifirouter 2021-05-22 02:51:31 +02:00
9b33f13f66 Italian translation updated 2021-05-21 19:52:36 +02:00
2f3a33b1b8 Translation 2021-05-20 23:11:16 +02:00
6c8ca59e3f Spanish translation 2021-05-19 21:06:15 +02:00
5ffb36a87f Added read_phone_state for action setDataConnection 2021-05-18 22:30:01 +02:00
1560fd3343 SuperSu related changes. 2021-05-18 20:02:45 +02:00
a0ff8c80f0 Spanish translation 2021-05-16 22:51:03 +02:00
1ea3bdaea3 Permissions constants used. 2021-05-16 19:57:05 +02:00
f325b30917 Permissions constants used. 2021-05-16 19:51:22 +02:00
8ce2a09b3b Wifi trigger mgmt change 2021-05-16 14:27:54 +02:00
4a18a6ed19 Wifi trigger mgmt change 2021-05-16 02:32:31 +02:00
9a8519d3e3 Phone Listener changed. 2021-05-15 19:55:48 +02:00
0a0399c2b0 Phone Listener changed. 2021-05-15 14:22:43 +02:00
3844079781 Phone Listener changed. 2021-05-15 02:27:59 +02:00
191ad904a3 Phone Listener changed. 2021-05-14 20:07:46 +02:00
e988cedf7c Phone Listener changed. 2021-05-14 13:00:25 +02:00
9a7f66fa22 PhoneCallTrigger 2021-05-13 23:50:14 +02:00
c926c85dd0 Phone Listener changed. 2021-05-13 18:37:34 +02:00
cddd8cfac5 Build 2021-05-13 14:44:29 +02:00
e57b888393 Spanish translation 2021-05-13 14:40:23 +02:00
c922f8c7fc Spanish translation 2021-05-13 14:33:44 +02:00
357c7f894f Crash resolved when trying display an unknown translation for a permission 2021-05-13 12:06:15 +02:00
34091a7b73 Spanish translation. 2021-05-13 02:44:10 +02:00
8d26abdede Spanish translation 2021-05-12 22:55:44 +02:00
8d42bb48d2 WG now works. Many small changes and fixes. 2021-05-12 19:41:04 +02:00
f79efa7739 WG now works. Many small changes and fixes. 2021-05-12 17:16:56 +02:00
d257c4ccb0 Miscellaneous changes. 2021-05-11 22:49:41 +02:00
e39f0c2497 startApp changes 2021-05-11 20:00:50 +02:00
327a992cac Synced manifests. 2021-05-11 17:02:06 +02:00
9021b03732 Export/import finished. 2021-05-11 16:53:19 +02:00
514a9ae0e4 Import/export configuration and Spanish translation. 2021-05-10 22:42:16 +02:00
09298bce55 UI changes. 2021-05-10 19:56:54 +02:00
74f50d3e13 Wireguard integration 2021-05-09 23:10:58 +02:00
1946fb6b9f Start app changed 2021-05-09 14:42:24 +02:00
acc0f592d1 Wireguard integration 2021-05-08 22:48:05 +02:00
2e09ea1c90 Start app changed 2021-05-08 19:58:44 +02:00
769f227689 Start app changed 2021-05-08 15:14:31 +02:00
22533fcfee Merge remote-tracking branch 'origin/development' into development 2021-05-08 02:03:18 +02:00
004ca6993a Small fixes and layout corrections. 2021-05-08 02:03:11 +02:00
0bea81a630 Small fixes and layout corrections. 2021-05-08 02:00:57 +02:00
88decce426 Wireguard integration 2021-05-07 23:11:43 +02:00
2341d714c0 Small fixes and layout corrections. 2021-04-30 13:56:59 +02:00
34c0067736 Small fixes and layout corrections. 2021-04-30 13:52:06 +02:00
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
218 changed files with 18179 additions and 7824 deletions

2
.gitignore vendored
View File

@ -148,3 +148,5 @@ fabric.properties
/app/app-release.apk
Automation_settings.xml
/app/googlePlayFlavor/
/.idea/deploymentTargetDropDown.xml

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

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

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

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

View File

@ -11,8 +11,8 @@ android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
useLibrary 'org.apache.http.legacy'
versionCode 98
versionName "1.6.22"
versionCode 116
versionName "1.7.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -36,41 +36,42 @@ android {
flavorDimensions "version"
productFlavors
{
googlePlayFlavor
{
dimension "version"
// applicationIdSuffix ".googlePlay"
versionNameSuffix "-googlePlay"
targetSdkVersion 30
}
{
googlePlayFlavor
{
dimension "version"
versionNameSuffix "-googlePlay"
targetSdkVersion 30
}
fdroidFlavor
{
dimension "version"
// applicationIdSuffix ".fdroid"
// versionNameSuffix "-fdroid"
targetSdkVersion 28
}
fdroidFlavor
{
dimension "version"
targetSdkVersion 28
}
apkFlavor
{
dimension "version"
// applicationIdSuffix ".apk"
versionNameSuffix "-apk"
targetSdkVersion 28
}
}
apkFlavor
{
dimension "version"
versionNameSuffix "-apk"
targetSdkVersion 28
}
}
}
dependencies {
googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0'
googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0'
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0'
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.linkedin.dexmaker:dexmaker:2.25.0'
implementation 'org.apache.commons:commons-lang3:3.0'
//implementation "androidx.security:security-crypto:1.0.0"
//implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'

View File

@ -1,18 +1,20 @@
{
"version": 2,
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.jens.automation2",
"variantName": "processGooglePlayFlavorReleaseResources",
"variantName": "googlePlayFlavorRelease",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 98,
"versionName": "1.6.22-googlePlay",
"attributes": [],
"versionCode": 116,
"versionName": "1.7.2-googlePlay",
"outputFile": "app-googlePlayFlavor-release.apk"
}
]
],
"elementType": "File"
}

View File

@ -51,6 +51,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />-->
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.NFC" />
@ -62,8 +63,9 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Commented out because of Google Play policy -->
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-feature
android:name="android.hardware.telephony"
@ -76,7 +78,7 @@
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
@ -95,15 +97,15 @@
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
android:label="@string/app_name" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
@ -120,27 +122,39 @@
<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.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />
<receiver
android:name=".DeviceAdmin"
android:description="@string/app_name"
android:label="@string/app_name"
android:permission= "android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/policies" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerProfile" />
<activity android:name=".ActivityManageTriggerTimeFrame" />
<activity android:name=".ActivityMaintenance" />
<activity android:name=".ActivityManageTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityManageTriggerDeviceOrientation" />
<activity android:name=".ActivityHelp" />
<activity android:name=".ActivityManageActionVibrate" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
@ -177,13 +191,33 @@
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity android:name=".ActivityPermissions" />
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<service
android:name=".receivers.ActivityDetectionReceiver"
@ -193,15 +227,15 @@
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity android:name=".ActivityPermissions"></activity>
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<service android:name=".location.GeofenceIntentService"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@ -291,10 +291,10 @@ public class ActivityDetectionReceiver extends IntentService implements Automati
* and some activities are hierarchical (ON_FOOT is a generalization of WALKING and RUNNING).
*/
ArrayList<Rule> allRulesWithActivityDetection = Rule.findRuleCandidatesByActivityDetection();
ArrayList<Rule> allRulesWithActivityDetection = Rule.findRuleCandidates(Trigger_Enum.activityDetection);
for(int i=0; i<allRulesWithActivityDetection.size(); i++)
{
if(allRulesWithActivityDetection.get(i).applies(Miscellaneous.getAnyContext()))
if(allRulesWithActivityDetection.get(i).getsGreenLight(Miscellaneous.getAnyContext()))
allRulesWithActivityDetection.get(i).activate(AutomationService.getInstance(), false);
}
}

View File

@ -51,6 +51,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />-->
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.NFC" />
@ -60,6 +61,9 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-feature
android:name="android.hardware.telephony"
@ -72,7 +76,7 @@
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
@ -91,15 +95,15 @@
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
android:label="@string/app_name" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
@ -116,27 +120,39 @@
<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.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />
<receiver
android:name=".DeviceAdmin"
android:description="@string/app_name"
android:label="@string/app_name"
android:permission= "android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/policies" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerProfile" />
<activity android:name=".ActivityManageTriggerTimeFrame" />
<activity android:name=".ActivityMaintenance" />
<activity android:name=".ActivityManageTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityManageTriggerDeviceOrientation" />
<activity android:name=".ActivityHelp" />
<activity android:name=".ActivityManageActionVibrate" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
@ -163,6 +179,7 @@
</intent-filter>
-->
<!--
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
@ -172,18 +189,39 @@
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity android:name=".ActivityPermissions" />
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,6 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
@ -51,6 +50,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />-->
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.NFC" />
@ -62,21 +62,15 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Commented out because of Google Play policy -->
<!--
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS"/>
-->
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<application
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
@ -95,15 +89,15 @@
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
android:label="@string/app_name" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
@ -120,27 +114,39 @@
<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.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />
<receiver
android:name=".DeviceAdmin"
android:description="@string/app_name"
android:label="@string/app_name"
android:permission= "android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/policies" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerProfile" />
<activity android:name=".ActivityManageTriggerTimeFrame" />
<activity android:name=".ActivityMaintenance" />
<activity android:name=".ActivityManageTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityManageTriggerDeviceOrientation" />
<activity android:name=".ActivityHelp" />
<activity android:name=".ActivityManageActionVibrate" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
@ -177,13 +183,33 @@
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityManageTriggerWifi" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity android:name=".ActivityPermissions" />
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<service
android:name=".receivers.ActivityDetectionReceiver"
@ -193,12 +219,14 @@
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity android:name=".ActivityPermissions"></activity>
<service android:name=".location.GeofenceIntentService"/>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ package com.jens.automation2.receivers;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@ -13,6 +14,7 @@ import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.location.ActivityRecognitionApi;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import com.jens.automation2.ActivityDisplayLongMessage;
import com.jens.automation2.ActivityPermissions;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
@ -110,6 +112,14 @@ public class ActivityDetectionReceiver extends IntentService implements Automati
}
public static void startActivityDetectionReceiver()
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if(!ActivityPermissions.havePermission("android.permission.ACTIVITY_RECOGNITION", Miscellaneous.getAnyContext()))
{
Miscellaneous.logEvent("w", "Activity Detection", "Don't have android.permission.ACTIVITY_RECOGNITION. Aborting receiver start..", 2);
return;
}
}
try
{
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Starting ActivityDetectionReceiver", 3);
@ -291,7 +301,7 @@ public class ActivityDetectionReceiver extends IntentService implements Automati
* and some activities are hierarchical (ON_FOOT is a generalization of WALKING and RUNNING).
*/
ArrayList<Rule> allRulesWithActivityDetection = Rule.findRuleCandidatesByActivityDetection();
ArrayList<Rule> allRulesWithActivityDetection = Rule.findRuleCandidates(Trigger_Enum.activityDetection);
for(int i=0; i<allRulesWithActivityDetection.size(); i++)
{
if(allRulesWithActivityDetection.get(i).applies(Miscellaneous.getAnyContext()))

View File

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

View File

@ -3,7 +3,9 @@ package com.jens.automation2;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpGet;
import java.util.ArrayList;
@ -12,11 +14,18 @@ import java.util.Locale;
public class Action
{
Rule parentRule = null;
public static final String actionParameter2Split = "ap2split";
public static final String intentPairSeperator = "intPairSplit";
public static final String vibrateSeparator = ",";
public enum Action_Enum {
setWifi,
setBluetooth,
setUsbTethering,
setWifiTethering,
setBluetoothTethering,
setDisplayRotation,
turnWifiOn,turnWifiOff,
turnBluetoothOn,turnBluetoothOff,
@ -24,15 +33,17 @@ public class Action
changeSoundProfile,
turnUsbTetheringOn,turnUsbTetheringOff,
turnWifiTetheringOn,turnWifiTetheringOff,
enableScreenRotation, disableScreenRotation,
enableScreenRotation,disableScreenRotation,
startOtherActivity,
waitBeforeNextAction,
wakeupDevice,
turnScreenOnOrOff,
setAirplaneMode,
setDataConnection,
speakText,
playMusic,
setScreenBrightness,
playSound,
vibrate,
sendTextMessage;
public String getFullName(Context context)
@ -45,6 +56,8 @@ public class Action
return context.getResources().getString(R.string.actionSetBluetooth);
case setWifiTethering:
return context.getResources().getString(R.string.actionSetWifiTethering);
case setBluetoothTethering:
return context.getResources().getString(R.string.actionSetBluetoothTethering);
case setUsbTethering:
return context.getResources().getString(R.string.actionSetUsbTethering);
case setDisplayRotation:
@ -77,8 +90,10 @@ public class Action
return context.getResources().getString(R.string.startOtherActivity);
case waitBeforeNextAction:
return context.getResources().getString(R.string.waitBeforeNextAction);
case wakeupDevice:
return context.getResources().getString(R.string.wakeupDevice);
case turnScreenOnOrOff:
return context.getResources().getString(R.string.turnScreenOnOrOff);
case vibrate:
return context.getResources().getString(R.string.vibrate);
case setAirplaneMode:
return context.getResources().getString(R.string.airplaneMode);
case setDataConnection:
@ -87,6 +102,8 @@ public class Action
return context.getResources().getString(R.string.actionSpeakText);
case playMusic:
return context.getResources().getString(R.string.actionPlayMusic);
case playSound:
return context.getResources().getString(R.string.playSound);
case sendTextMessage:
return context.getResources().getString(R.string.sendTextMessage);
case setScreenBrightness:
@ -136,83 +153,94 @@ public class Action
public String toString()
{
StringBuilder returnString = new StringBuilder();
if(this.getAction().equals(Action_Enum.setWifi))
switch(getAction())
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiOff));
case setWifi:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiOff));
break;
case setBluetooth:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnBluetoothOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnBluetoothOff));
break;
case setUsbTethering:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnUsbTetheringOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnUsbTetheringOff));
break;
case setWifiTethering:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiTetheringOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiTetheringOff));
break;
case setBluetoothTethering:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnBluetoothTetheringOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnBluetoothTetheringOff));
break;
case setDisplayRotation:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionEnableScreenRotation));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionDisableScreenRotation));
break;
case setAirplaneMode:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnAirplaneModeOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnAirplaneModeOff));
break;
case setDataConnection:
if (this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionSetDataConnectionOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionSetDataConnectionOff));
break;
case startOtherActivity:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.startOtherActivity));
break;
case triggerUrl:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTriggerUrl));
break;
case speakText:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionSpeakText));
break;
case playMusic:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionPlayMusic));
break;
case sendTextMessage:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.sendTextMessage));
break;
case turnScreenOnOrOff:
if (getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.turnScreenOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.turnScreenOff));
break;
case playSound:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.playSound));
break;
case changeSoundProfile:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionChangeSoundProfile));
break;
case waitBeforeNextAction:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.waitBeforeNextAction));
break;
case setScreenBrightness:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.setScreenBrightness));
break;
default:
returnString.append(action.toString());
}
else if(this.getAction().equals(Action_Enum.setBluetooth))
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnBluetoothOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnBluetoothOff));
}
else if(this.getAction().equals(Action_Enum.setUsbTethering))
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnUsbTetheringOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnUsbTetheringOff));
}
else if(this.getAction().equals(Action_Enum.setWifiTethering))
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiTetheringOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnWifiTetheringOff));
}
else if(this.getAction().equals(Action_Enum.setDisplayRotation))
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionEnableScreenRotation));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionDisableScreenRotation));
}
else if(this.getAction().equals(Action_Enum.setAirplaneMode))
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnAirplaneModeOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTurnAirplaneModeOff));
}
else if(this.getAction().equals(Action_Enum.setDataConnection))
{
if(this.getParameter1())
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionSetDataConnectionOn));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionSetDataConnectionOff));
}
else if(this.getAction().equals(Action_Enum.startOtherActivity))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.startOtherActivity));
}
else if(this.getAction().equals(Action_Enum.triggerUrl))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionTriggerUrl));
}
else if(this.getAction().equals(Action_Enum.speakText))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionSpeakText));
}
else if(this.getAction().equals(Action_Enum.playMusic))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.actionPlayMusic));
}
else if(this.getAction().equals(Action_Enum.sendTextMessage))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.sendTextMessage));
}
else if(this.getAction().equals(Action_Enum.wakeupDevice))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.wakeupDevice));
}
else
returnString.append(action.toString());
if(this.getAction().equals(Action_Enum.triggerUrl))
{
String[] components = parameter2.split(";");
@ -221,24 +249,28 @@ public class Action
returnString.append(": " + components[2]);
if(parameter1)
returnString.append(" using authentication.");
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.usingAuthentication) + ".");
}
else
returnString.append(": " + components[0]);
}
else if(this.getAction().equals(Action_Enum.startOtherActivity))
{
returnString.append(": " + parameter2.replace(Action.intentPairSeperator, "/"));
}
else if(this.getAction().equals(Action_Enum.sendTextMessage))
{
String[] components = parameter2.split(Actions.smsSeparator);
if(components.length >= 2)
{
returnString.append(" to number " + components[0]);
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.toNumber) + " " + components[0]);
returnString.append(". Message: " + components[1]);
returnString.append(". " + Miscellaneous.getAnyContext().getResources().getString(R.string.message) + ": " + components[1]);
}
}
else if(this.getAction().equals(Action_Enum.setScreenBrightness))
{
returnString.append(" to ");
returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.to) + " ");
if(parameter1)
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.brightnessAuto));
@ -254,6 +286,16 @@ public class Action
return returnString.toString();
}
public Rule getParentRule()
{
return parentRule;
}
public void setParentRule(Rule parentRule)
{
this.parentRule = parentRule;
}
public static CharSequence[] getActionTypesAsArray()
{
ArrayList<String> actionTypesList = new ArrayList<String>();
@ -320,84 +362,104 @@ public class Action
}
public void run(Context context, boolean toggleActionIfPossible)
{
switch(this.getAction())
{
try
{
case changeSoundProfile:
/*
* Old version. Those checks should not be necessary anymore. Also they didn't work
* because profiles were created with names like silent, vibrate and normal.
*/
// if(this.getParameter2().equals("silent"))
// Actions.setSound(context, AudioManager.RINGER_MODE_SILENT);
// else if(this.getParameter2().equals("vibrate"))
// Actions.setSound(context, AudioManager.RINGER_MODE_VIBRATE);
// else if(this.getParameter2().equals("normal"))
// Actions.setSound(context, AudioManager.RINGER_MODE_NORMAL);
// else
// {
switch(this.getAction())
{
case changeSoundProfile:
/*
* Old version. Those checks should not be necessary anymore. Also they didn't work
* because profiles were created with names like silent, vibrate and normal.
*/
Profile p = Profile.getByName(this.getParameter2());
if(p != null)
if (p != null)
p.activate(context);
// }
break;
case triggerUrl:
triggerUrl(context);
break;
case setBluetooth:
Actions.setBluetooth(context, getParameter1(), toggleActionIfPossible);
break;
case setUsbTethering:
Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setWifi:
Actions.setWifi(context, getParameter1(), toggleActionIfPossible);
break;
case setWifiTethering:
Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setDisplayRotation:
Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible);
break;
case startOtherActivity:
Actions.startOtherActivity(getParameter2());
break;
case waitBeforeNextAction:
Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2()));
break;
case wakeupDevice:
Actions.wakeupDevice(Long.parseLong(this.getParameter2()));
// wakeupDevice() will create a seperate thread. That'll take some time, we wait 100ms.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
break;
case setAirplaneMode:
Actions.setAirplaneMode(this.getParameter1(), toggleActionIfPossible);
break;
case setDataConnection:
Actions.MobileDataStuff.setDataConnection(this.getParameter1(), toggleActionIfPossible);
break;
case speakText:
Actions.speakText(this.getParameter2());
break;
case playMusic:
Actions.playMusic(this.getParameter1(), toggleActionIfPossible);
break;
case sendTextMessage:
Actions.sendTextMessage(context, this.getParameter2().split(Actions.smsSeparator));
break;
case setScreenBrightness:
Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2()));
break;
default:
Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3);
break;
break;
case triggerUrl:
triggerUrl(context);
break;
case setBluetooth:
Actions.setBluetooth(context, getParameter1(), toggleActionIfPossible);
break;
case setUsbTethering:
Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setWifi:
Actions.WifiStuff.setWifi(context, getParameter1(), toggleActionIfPossible);
break;
case setWifiTethering:
Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setBluetoothTethering:
Actions.BluetoothTetheringClass.setBluetoothTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setDisplayRotation:
Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible);
break;
case startOtherActivity:
Actions.startOtherActivity(getParameter1(), getParameter2());
break;
case waitBeforeNextAction:
Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2()));
break;
case turnScreenOnOrOff:
if(getParameter1())
{
if(StringUtils.isNumeric(this.getParameter2()))
Actions.wakeupDevice(Long.parseLong(this.getParameter2()));
else
Actions.wakeupDevice((long)1000);
// wakeupDevice() will create a separate thread. That'll take some time, we wait 100ms.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
else
{
Actions.turnOffScreen();
}
break;
case setAirplaneMode:
Actions.setAirplaneMode(this.getParameter1(), toggleActionIfPossible);
break;
case setDataConnection:
Actions.MobileDataStuff.setDataConnection(this.getParameter1(), toggleActionIfPossible);
break;
case speakText:
Actions.speakText(this.getParameter2());
break;
case playMusic:
Actions.playMusic(this.getParameter1(), toggleActionIfPossible);
break;
case sendTextMessage:
Actions.sendTextMessage(context, this.getParameter2().split(Actions.smsSeparator));
break;
case setScreenBrightness:
Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2()));
break;
case vibrate:
Actions.vibrate(getParameter1(), getParameter2());
break;
case playSound:
Actions.playSound(getParameter1(), getParameter2());
break;
default:
Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3);
break;
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "ActionExecution", Log.getStackTraceString(e), 1);
Toast.makeText(context, context.getResources().getString(R.string.errorRunningRule), Toast.LENGTH_LONG).show();
}
}
@ -433,7 +495,7 @@ public class Action
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "triggerUrl", context.getResources().getString(R.string.errorTriggeringUrl) + ": " + e.getMessage() + ", detailed: " + Log.getStackTraceString(e), 2);
Miscellaneous.logEvent("e", "triggerUrl", context.getResources().getString(R.string.logErrorTriggeringUrl) + ": " + e.getMessage() + ", detailed: " + Log.getStackTraceString(e), 2);
}
}
@ -464,62 +526,24 @@ public class Action
while(attempts <= Settings.httpAttempts && response.equals("httpError"))
{
Miscellaneous.logEvent("i", "HTTP Request", "Attempt " + String.valueOf(attempts++) + " of " + String.valueOf(Settings.httpAttempts), 3);
// try
// {
// Either thorough checking or no encryption
if(!Settings.httpAcceptAllCertificates | !urlString.toLowerCase(Locale.getDefault()).contains("https"))
// {
// URL url = new URL(urlString);
// URLConnection urlConnection = url.openConnection();
// urlConnection.setReadTimeout(Settings.httpAttemptsTimeout * 1000);
// InputStream in = urlConnection.getInputStream();
// response = Miscellaneous.convertStreamToString(in);
response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword);
// }
else
// {
response = Miscellaneous.downloadURLwithoutCertificateChecking(urlString, urlUsername, urlPassword);
// post = new HttpGet(new URI(urlString));
// final HttpParams httpParams = new BasicHttpParams();
// HttpConnectionParams.setConnectionTimeout(httpParams, Settings.httpAttemptsTimeout * 1000);
// HttpClient client = new DefaultHttpClient(httpParams);
//
// client = sslClient(client);
//
// // Execute HTTP Post Request
// HttpResponse result = client.execute(post);
// response = EntityUtils.toString(result.getEntity());
// }
// }
// catch (URISyntaxException e)
// {
// Miscellaneous.logEvent("w", "HTTP RESULT", Log.getStackTraceString(e), 3);
// }
// catch (ClientProtocolException e)
// {
// Miscellaneous.logEvent("w", "HTTP RESULT", Log.getStackTraceString(e), 3);
// }
// catch (IOException e)
// {
// Miscellaneous.logEvent("w", "HTTP RESULT", Log.getStackTraceString(e), 3);
// e.printStackTrace();
// }
// finally
// {
try
{
Thread.sleep(Settings.httpAttemptGap * 1000);
}
catch (InterruptedException e1)
{
Miscellaneous.logEvent("w", "HTTP RESULT", "Failed to pause between HTTP requests.", 5);
}
// }
// Either thorough checking or no encryption
if(!Settings.httpAcceptAllCertificates || !urlString.toLowerCase(Locale.getDefault()).contains("https"))
response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword);
else
response = Miscellaneous.downloadURLwithoutCertificateChecking(urlString, urlUsername, urlPassword);
try
{
Thread.sleep(Settings.httpAttemptGap * 1000);
}
catch (InterruptedException e1)
{
Miscellaneous.logEvent("w", "HTTP RESULT", "Failed to pause between HTTP requests.", 5);
}
}
// Miscellaneous.logEvent("i", "HTTPS RESULT", response, 3);
Miscellaneous.logEvent("i", "HTTPS RESULT", response, 5);
return response;
}

File diff suppressed because it is too large Load Diff

View File

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

@ -9,7 +9,6 @@ import com.jens.automation2.R.layout;
public class ActivityHelp extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
@ -19,5 +18,4 @@ public class ActivityHelp extends Activity
TextView tvHelpTextEnergySaving = (TextView) findViewById(R.id.tvHelpTextEnergySaving);
tvHelpTextEnergySaving.setMovementMethod(LinkMovementMethod.getInstance());
}
}
}

View File

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

@ -184,8 +184,16 @@ public class ActivityMainProfiles extends ActivityGeneric
startActivityForResult(manageSpecificProfileIntent, 2000);
break;
case 2:
if(profile.delete(myAutomationService))
updateListView();
Rule user = profile.isInUseByRules();
if(user == null)
{
if (profile.delete(ActivityMainProfiles.this))
updateListView();
else
Toast.makeText(ActivityMainProfiles.this, getResources().getString(R.string.profileCouldNotBeDeleted), Toast.LENGTH_LONG).show();
}
else
Toast.makeText(ActivityMainProfiles.this, String.format(getResources().getString(R.string.ruleXIsUsingProfileY), user.getName(), profile.getName()), Toast.LENGTH_LONG).show();
break;
}
}

View File

@ -19,17 +19,21 @@ import android.widget.TextView;
import android.widget.Toast;
import com.jens.automation2.AutomationService.serviceCommands;
import com.jens.automation2.receivers.AlarmListener;
import com.jens.automation2.receivers.DateTimeListener;
import java.util.ArrayList;
public class ActivityMainRules extends ActivityGeneric
{
private ListView ruleListView;
ArrayList<Rule> ruleList = new ArrayList<>();
private ArrayAdapter<Rule> ruleListViewAdapter;
public static Rule ruleToEdit;
protected static ActivityMainRules instance = null;
public static final int requestCodeCreateRule = 3000;
public static final int requestCodeChangeRule = 4000;
public static ActivityMainRules getInstance()
{
if(instance == null)
@ -52,21 +56,14 @@ public class ActivityMainRules extends ActivityGeneric
@Override
public void onClick(View v)
{
// if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainRules.this))
// {
// Toast.makeText(ActivityMainRules.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// return;
// }
ruleToEdit = null;
Intent startAddRuleIntent = new Intent(ActivityMainRules.this, ActivityManageRule.class);
startActivityForResult(startAddRuleIntent, 3000);
startActivityForResult(startAddRuleIntent, requestCodeCreateRule);
}
});
ruleListViewAdapter = new RuleArrayAdapter(this, R.layout.view_for_rule_listview, ruleList);
ruleListView = (ListView)findViewById(R.id.lvRuleList);
ruleListViewAdapter = new RuleArrayAdapter(this, R.layout.view_for_rule_listview, Rule.getRuleCollection());
ruleListView.setClickable(true);
ruleListView.setOnItemLongClickListener(new OnItemLongClickListener()
@ -112,7 +109,6 @@ public class ActivityMainRules extends ActivityGeneric
private static class RuleArrayAdapter extends ArrayAdapter<Rule>
{
public RuleArrayAdapter(Context context, int resource, ArrayList<Rule> objects)
{
super(context, resource, objects);
@ -163,13 +159,13 @@ public class ActivityMainRules extends ActivityGeneric
if(AutomationService.isMyServiceRunning(this))
bindToService();
if(requestCode == 3000) //add Rule
if(requestCode == requestCodeCreateRule) //add Rule
{
ruleToEdit = null; //clear cache
updateListView();
}
if(requestCode == 4000) //editRule
if(requestCode == requestCodeChangeRule) //editRule
{
ruleToEdit = null; //clear cache
updateListView();
@ -190,7 +186,7 @@ public class ActivityMainRules extends ActivityGeneric
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.whatToDoWithRule));
alertDialogBuilder.setItems(new String[]{ getResources().getString(R.string.runManually), getResources().getString(R.string.edit), getResources().getString(R.string.deleteCapital) }, new DialogInterface.OnClickListener()
alertDialogBuilder.setItems(new String[]{ getResources().getString(R.string.runManually), getResources().getString(R.string.edit), getResources().getString(R.string.deleteCapital), getResources().getString(R.string.clone) }, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
@ -212,11 +208,22 @@ public class ActivityMainRules extends ActivityGeneric
case 1:
ruleToEdit = ruleThisIsAbout;
Intent manageSpecificRuleIntent = new Intent (ActivityMainRules.this, ActivityManageRule.class);
startActivityForResult(manageSpecificRuleIntent, 4000);
startActivityForResult(manageSpecificRuleIntent, requestCodeChangeRule);
break;
case 2:
if(ruleThisIsAbout.delete())
{
ruleToEdit = null; //clear cache
updateListView();
}
break;
case 3:
ruleToEdit = ruleThisIsAbout;
if(ruleToEdit.cloneRule(ActivityMainRules.this))
{
ruleToEdit = null; //clear cache
updateListView();
}
break;
}
}
@ -229,6 +236,11 @@ public class ActivityMainRules extends ActivityGeneric
public void updateListView()
{
Miscellaneous.logEvent("i", "ListView", "Attempting to update RuleListView", 4);
ruleList.clear();
for(Rule r : Rule.getRuleCollection())
ruleList.add(r);
try
{
if(ruleListView.getAdapter() == null)
@ -242,11 +254,11 @@ public class ActivityMainRules extends ActivityGeneric
try
{
if(AutomationService.isMyServiceRunning(this))
AlarmListener.reloadAlarms();
DateTimeListener.reloadAlarms();
}
catch(NullPointerException e)
{
// AlarmManager instance not prepared, yet.
}
}
}
}

View File

@ -1,15 +1,17 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.util.Xml;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
@ -27,8 +29,10 @@ import androidx.core.text.HtmlCompat;
import com.jens.automation2.AutomationService.serviceCommands;
import com.jens.automation2.Trigger.Trigger_Enum;
import com.jens.automation2.location.CellLocationChangedReceiver;
import com.jens.automation2.location.LocationProvider;
import java.io.File;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Calendar;
@ -40,8 +44,9 @@ public class ActivityMainScreen extends ActivityGeneric
private static ActivityMainScreen activityMainScreenInstance = null;
private ToggleButton toggleService, tbLockSound;
private Button bShowHelp, bPrivacy, bSettingsErase, bSettingsSetToDefault, bVolumeTest, bAddSoundLockTIme;
private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNote1, tvMainScreenNote2, tvMainScreenNote3, tvlockSoundDuration;
private Button bShowHelp, bPrivacy, bSettingsErase, bAddSoundLockTIme, bDonate;
private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNotePermissions, tvMainScreenNoteFeaturesFromOtherFlavor, tvMainScreenNoteLocationImpossibleBlameGoogle, tvMainScreenNoteNews, tvlockSoundDuration;
private static boolean updateNoteDisplayed = false;
private ListView lvRuleHistory;
private ArrayAdapter<Rule> ruleHistoryListViewAdapter;
@ -70,12 +75,19 @@ public class ActivityMainScreen extends ActivityGeneric
tvClosestPoi = (TextView) findViewById(R.id.tvClosestPoi);
lvRuleHistory = (ListView) findViewById(R.id.lvRuleHistory);
tvLastRule = (TextView) findViewById(R.id.tvTimeFrameHelpText);
tvMainScreenNote1 = (TextView) findViewById(R.id.tvMainScreenNote1);
tvMainScreenNote2 = (TextView) findViewById(R.id.tvMainScreenNote2);
tvMainScreenNote3 = (TextView) findViewById(R.id.tvMainScreenNote3);
tvMainScreenNotePermissions = (TextView) findViewById(R.id.tvMainScreenNotePermissions);
tvMainScreenNoteFeaturesFromOtherFlavor = (TextView) findViewById(R.id.tvMainScreenNoteFeaturesFromOtherFlavor);
tvMainScreenNoteLocationImpossibleBlameGoogle = (TextView) findViewById(R.id.tvMainScreenNoteLocationImpossibleBlameGoogle);
tvMainScreenNoteNews = (TextView) findViewById(R.id.tvMainScreenNoteNews);
tvlockSoundDuration = (TextView)findViewById(R.id.tvlockSoundDuration);
tbLockSound = (ToggleButton) findViewById(R.id.tbLockSound);
toggleService = (ToggleButton) findViewById(R.id.tbArmMastListener);
bDonate = (Button)findViewById(R.id.bDonate);
if(!BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor"))
bDonate.setVisibility(View.VISIBLE);
toggleService.setChecked(AutomationService.isMyServiceRunning(this));
toggleService.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
@ -95,7 +107,7 @@ public class ActivityMainScreen extends ActivityGeneric
}
});
tvMainScreenNote1.setOnClickListener(new OnClickListener()
tvMainScreenNotePermissions.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
@ -105,6 +117,18 @@ public class ActivityMainScreen extends ActivityGeneric
}
});
bDonate.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
String privacyPolicyUrl = "https://server47.de/donate";
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(privacyPolicyUrl));
startActivity(browserIntent);
}
});
tbLockSound.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
@Override
@ -129,19 +153,8 @@ public class ActivityMainScreen extends ActivityGeneric
@Override
public void onClick(View v)
{
Intent myIntent = new Intent(ActivityMainScreen.this, ActivitySettings.class);
startActivityForResult(myIntent, 6000);
}
});
Button bVolumeTest = (Button) findViewById(R.id.bVolumeTest);
bVolumeTest.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(ActivityMainScreen.this, ActivityVolumeTest.class);
startActivity(intent);
Intent myIntent = new Intent(ActivityMainScreen.this, ActivityMaintenance.class);
startActivity(myIntent);
}
});
@ -177,25 +190,6 @@ public class ActivityMainScreen extends ActivityGeneric
builder.create().show();
}
});
/*bSettingsErase = (Button)findViewById(R.id.bSettingsErase);
bSettingsErase.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
getEraseSettingsDialog(ActivityMainScreen.this).show();
}
});*/
bSettingsSetToDefault = (Button) findViewById(R.id.bSettingsSetToDefault);
bSettingsSetToDefault.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
getDefaultSettingsDialog(ActivityMainScreen.this).show();
}
});
lvRuleHistory.setOnTouchListener(new OnTouchListener()
{
@ -259,25 +253,6 @@ public class ActivityMainScreen extends ActivityGeneric
return alertDialog;
}
private static AlertDialog getDefaultSettingsDialog(final Context context)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(context.getResources().getString(R.string.areYouSure));
alertDialogBuilder.setPositiveButton(context.getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
if (Settings.initializeSettings(context, true))
Toast.makeText(context, context.getResources().getString(R.string.settingsSetToDefault), Toast.LENGTH_LONG).show();
}
});
alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null);
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
public static ActivityMainScreen getActivityMainScreenInstance()
{
return activityMainScreenInstance;
@ -291,24 +266,52 @@ public class ActivityMainScreen extends ActivityGeneric
{
if(ActivityPermissions.needMorePermissions(activityMainScreenInstance))
{
activityMainScreenInstance.tvMainScreenNote1.setText(R.string.mainScreenPermissionNote);
activityMainScreenInstance.tvMainScreenNote1.setVisibility(View.VISIBLE);
activityMainScreenInstance.tvMainScreenNotePermissions.setText(R.string.mainScreenPermissionNote);
activityMainScreenInstance.tvMainScreenNotePermissions.setVisibility(View.VISIBLE);
}
else
{
activityMainScreenInstance.tvMainScreenNote1.setText("");
activityMainScreenInstance.tvMainScreenNote1.setVisibility(View.GONE);
activityMainScreenInstance.tvMainScreenNotePermissions.setText("");
activityMainScreenInstance.tvMainScreenNotePermissions.setVisibility(View.GONE);
}
if(Miscellaneous.restrictedFeaturesConfigured())
{
activityMainScreenInstance.tvMainScreenNote2.setText(R.string.settingsReferringToRestrictedFeatures);
activityMainScreenInstance.tvMainScreenNote2.setVisibility(View.VISIBLE);
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setText(R.string.settingsReferringToRestrictedFeatures);
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setVisibility(View.VISIBLE);
}
else
{
activityMainScreenInstance.tvMainScreenNote2.setText("");
activityMainScreenInstance.tvMainScreenNote2.setVisibility(View.GONE);
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setText("");
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setVisibility(View.GONE);
}
if(Miscellaneous.googleToBlameForLocation(true))
{
// Intent intent = new Intent(AutomationService.this, ActivityDisplayLongMessage.class);
// intent.putExtra("longMessage", getResources().getString(R.string.locationEngineDisabledLong));
// PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
// Miscellaneous.createDismissableNotificationWithDelay(2200, getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
// else
// Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setText(R.string.locationEngineDisabledShort);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setVisibility(View.VISIBLE);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
openGoogleBlamingWindow();
}
});
}
else
{
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setText("");
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setVisibility(View.GONE);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setOnClickListener(null);
}
if (AutomationService.isMyServiceRunning(activityMainScreenInstance))
@ -341,9 +344,9 @@ public class ActivityMainScreen extends ActivityGeneric
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, AutomationService.getInstance())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, Miscellaneous.getAnyContext())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, AutomationService.getInstance())
ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, Miscellaneous.getAnyContext())
)
activityMainScreenInstance.tvActivePoi.setText(activityMainScreenInstance.getResources().getString(R.string.stillGettingPosition));
else
@ -418,7 +421,61 @@ public class ActivityMainScreen extends ActivityGeneric
Miscellaneous.logEvent("i", "ActivityMainScreen", "Activity not running. No need to update.", 5);
if(activityMainScreenInstance != null)
activityMainScreenInstance.checkForNews();
{
if(!Settings.hasBeenDone(Settings.constNewsOptInDone))
newsOptIn();
else
activityMainScreenInstance.checkForNews();
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
@ -436,16 +493,6 @@ public class ActivityMainScreen extends ActivityGeneric
case ActivityPermissions.requestCodeForPermissions:
updateMainScreen();
break;
case 6000: //settings
Settings.readFromPersistentStorage(this);
if (boundToService && AutomationService.isMyServiceRunning(this))
myAutomationService.serviceInterface(serviceCommands.reloadSettings);
if(AutomationService.isMyServiceRunning(ActivityMainScreen.this))
Toast.makeText(this, getResources().getString(R.string.settingsWillTakeTime), Toast.LENGTH_LONG).show();
break;
}
if (AutomationService.isMyServiceRunning(this))
@ -570,59 +617,76 @@ public class ActivityMainScreen extends ActivityGeneric
Miscellaneous.messageBox(title, text, ActivityMainScreen.getActivityMainScreenInstance());
}
synchronized void checkForUpdate()
{
if(Settings.automaticUpdateCheck)
{
AsyncTasks.AsyncTaskUpdateCheck updateCheckTask = new AsyncTasks.AsyncTaskUpdateCheck();
updateCheckTask.execute(ActivityMainScreen.this);
}
}
synchronized void checkForNews()
{
News.AsyncTaskDownloadNews dnTask = new News.AsyncTaskDownloadNews();
dnTask.execute(ActivityMainScreen.this);
// StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
// StrictMode.setThreadPolicy(policy);
// Calendar now = Calendar.getInstance();
// if (true || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.pollNewsEveryXDays * 24 * 60 * 60 * 1000))
// {
// ArrayList<News> newsToDisplay;
//
//// if(Settings.lastNewsPolltime == -1)
// newsToDisplay = News.downloadNews(ActivityMainScreen.this);
//// else
//// newsToDisplay = News.downloadNews(ActivityMainScreen.this, Miscellaneous.calendarFromLong(Settings.lastNewsPolltime));
//
// if (newsToDisplay.size() > 0)
// {
// activityMainScreenInstance.tvMainScreenNote3.setText(newsToDisplay.get(0).toString());
// activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.VISIBLE);
// }
// else
// {
// activityMainScreenInstance.tvMainScreenNote3.setText("");
// activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.GONE);
// }
// }
if(Settings.displayNewsOnMainScreen)
{
News.AsyncTaskDownloadNews dnTask = new News.AsyncTaskDownloadNews();
dnTask.execute(ActivityMainScreen.this);
}
}
public void processNewsResult(ArrayList<News> newsToDisplay)
{
if(Settings.displayNewsOnMainScreen)
try
{
try
if (newsToDisplay.size() > 0)
{
if (newsToDisplay.size() > 0)
{
activityMainScreenInstance.tvMainScreenNote3.setText(HtmlCompat.fromHtml(newsToDisplay.get(0).toStringHtml(), 0));
activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.VISIBLE);
}
else
{
activityMainScreenInstance.tvMainScreenNote3.setText("");
activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.GONE);
}
activityMainScreenInstance.tvMainScreenNoteNews.setText(HtmlCompat.fromHtml(newsToDisplay.get(0).toStringHtml(), 0));
activityMainScreenInstance.tvMainScreenNoteNews.setVisibility(View.VISIBLE);
}
catch(Exception e)
else
{
Miscellaneous.logEvent("e", "Error displaying news", Log.getStackTraceString(e), 3);
activityMainScreenInstance.tvMainScreenNoteNews.setText("");
activityMainScreenInstance.tvMainScreenNoteNews.setVisibility(View.GONE);
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "Error displaying news", Log.getStackTraceString(e), 3);
}
}
public void processUpdateCheckResult(Boolean result)
{
if(result && !updateNoteDisplayed)
{
updateNoteDisplayed = true;
AlertDialog.Builder updateNoteBuilder = new AlertDialog.Builder(ActivityMainScreen.this);
updateNoteBuilder.setMessage(getResources().getString(R.string.updateAvailable));
updateNoteBuilder.setPositiveButton(getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i)
{
String url = "https://server47.de/automation/";
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent);
updateNoteDisplayed = false;
}
});
updateNoteBuilder.setNegativeButton(getResources().getString(R.string.no), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i)
{
updateNoteDisplayed = false;
}
});
updateNoteBuilder.show();
}
AsyncTasks.AsyncTaskUpdateCheck.checkRunning = false;
}
}

View File

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

View File

@ -0,0 +1,377 @@
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();
ActivityMainPoi.getInstance().updateListView();
ActivityMainRules.getInstance().updateListView();
ActivityMainProfiles.getInstance().updateListView();
}
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";
Uri uri = Uri.parse("content://com.jens.automation2/" + Settings.zipFileName);
Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", getSystemInfo(), uri);
}
});
alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null);
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
public static String getSystemInfo()
{
StringBuilder systemInfoText = new StringBuilder();
systemInfoText.append("Device details" + Miscellaneous.lineSeparator);
systemInfoText.append("OS version: " + System.getProperty("os.version") + Miscellaneous.lineSeparator);
systemInfoText.append("API Level: " + android.os.Build.VERSION.SDK + Miscellaneous.lineSeparator);
systemInfoText.append("Device: " + android.os.Build.DEVICE + Miscellaneous.lineSeparator);
systemInfoText.append("Model: " + android.os.Build.MODEL + Miscellaneous.lineSeparator);
systemInfoText.append("Product: " + android.os.Build.PRODUCT + Miscellaneous.lineSeparator);
systemInfoText.append("Flavor: " + BuildConfig.FLAVOR);
return systemInfoText.toString();
}
@Override
protected void onResume()
{
super.onResume();
String folder = Miscellaneous.getWriteableFolder();
if (folder != null && folder.length() > 0)
{
tvFileStoreLocation.setText(String.format(getResources().getString(R.string.filesStoredAt), folder));
tvFileStoreLocation.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Uri selectedUri = Uri.parse(folder);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(selectedUri, "resource/folder");
if (intent.resolveActivityInfo(getPackageManager(), 0) != null)
{
startActivity(intent);
}
else
{
// if you reach this place, it means there is no any file
// explorer app installed on your device
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.noFileManageInstalled), Toast.LENGTH_LONG).show();
}
}
});
}
}
}

View File

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

View File

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

View File

@ -10,7 +10,7 @@ import android.widget.Toast;
import com.jens.automation2.Action.Action_Enum;
public class ActivityEditSpeakText extends Activity
public class ActivityManageActionSpeakText extends Activity
{
private Button bSaveSpeakText;
private EditText etSpeakText;
@ -24,7 +24,7 @@ public class ActivityEditSpeakText extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.speak_text_editor);
this.setContentView(R.layout.activity_manage_action_speak_text);
etSpeakText = (EditText)findViewById(R.id.etTextToSpeak);
bSaveSpeakText = (Button)findViewById(R.id.bSaveTriggerUrl);
@ -44,13 +44,13 @@ public class ActivityEditSpeakText extends Activity
backToRuleManager();
}
else
Toast.makeText(getBaseContext(), getResources().getString(R.string.textTooShort), Toast.LENGTH_LONG).show();
Toast.makeText(getBaseContext(), getResources().getString(R.string.enterPhoneNumberAndText), Toast.LENGTH_LONG).show();
}
});
ActivityEditSpeakText.edit = getIntent().getBooleanExtra("edit", false);
ActivityManageActionSpeakText.edit = getIntent().getBooleanExtra("edit", false);
if(edit)
etSpeakText.setText(ActivityEditSpeakText.resultingAction.getParameter2());
etSpeakText.setText(ActivityManageActionSpeakText.resultingAction.getParameter2());
// String url = getIntent().getStringExtra("urlToTrigger");
@ -66,7 +66,7 @@ public class ActivityEditSpeakText extends Activity
// setResult(RESULT_OK, returnIntent);
if(edit && resultingAction != null)
ActivityEditSpeakText.resultingAction.setParameter2(etSpeakText.getText().toString());
ActivityManageActionSpeakText.resultingAction.setParameter2(etSpeakText.getText().toString());
setResult(RESULT_OK);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package com.jens.automation2;
import android.app.Activity;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
@ -37,8 +38,8 @@ public class ActivityManageProfile extends Activity
final static int intentCodeRingtonePickerNotificationsFile = 9020;
final static int intentCodeRingtonePickerNotificationsRingtone = 9021;
CheckBox checkBoxChangeSoundMode, checkBoxChangeVolumeMusicVideoGameMedia, checkBoxChangeVolumeNotifications, checkBoxChangeVolumeAlarms, checkBoxChangeIncomingCallsRingtone, checkBoxChangeNotificationRingtone, checkBoxChangeAudibleSelection, checkBoxChangeScreenLockUnlockSound, checkBoxChangeHapticFeedback, checkBoxChangeVibrateWhenRinging, checkBoxVibrateWhenRinging, checkBoxAudibleSelection, checkBoxScreenLockUnlockSound, checkBoxHapticFeedback;
Spinner spinnerSoundMode;
CheckBox checkBoxChangeSoundMode, checkBoxChangeVolumeMusicVideoGameMedia, checkBoxChangeVolumeNotifications, checkBoxChangeVolumeAlarms, checkBoxChangeIncomingCallsRingtone, checkBoxChangeNotificationRingtone, checkBoxChangeAudibleSelection, checkBoxChangeScreenLockUnlockSound, checkBoxChangeHapticFeedback, checkBoxChangeVibrateWhenRinging, checkBoxVibrateWhenRinging, checkBoxAudibleSelection, checkBoxScreenLockUnlockSound, checkBoxHapticFeedback, checkBoxChangeDnd;
Spinner spinnerSoundMode, spinnerDndMode;
SeekBar seekBarVolumeMusic, seekBarVolumeNotifications, seekBarVolumeAlarms;
Button bChangeSoundIncomingCalls, bChangeSoundNotifications, bSaveProfile;
TextView tvIncomingCallsRingtone, tvNotificationsRingtone;
@ -47,6 +48,7 @@ public class ActivityManageProfile extends Activity
File incomingCallsRingtone = null, notificationsRingtone = null;
ArrayAdapter<String> soundModeAdapter;
ArrayAdapter<String> dndModeAdapter;
public void setIncomingCallsRingtone(File incomingCallsRingtone)
{
@ -82,9 +84,10 @@ public class ActivityManageProfile extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.manage_specific_profile);
this.setContentView(R.layout.activity_manage_specific_profile);
checkBoxChangeSoundMode = (CheckBox)findViewById(R.id.checkBoxChangeSoundMode);
checkBoxChangeDnd = (CheckBox)findViewById(R.id.checkBoxChangeDnd);
checkBoxChangeVolumeMusicVideoGameMedia = (CheckBox)findViewById(R.id.checkBoxChangeVolumeMusicVideoGameMedia);
checkBoxChangeVolumeNotifications = (CheckBox)findViewById(R.id.checkBoxChangeVolumeNotifications);
checkBoxChangeVolumeAlarms = (CheckBox)findViewById(R.id.checkBoxChangeVolumeAlarms);
@ -99,6 +102,7 @@ public class ActivityManageProfile extends Activity
checkBoxHapticFeedback = (CheckBox)findViewById(R.id.checkBoxHapticFeedback);
checkBoxVibrateWhenRinging = (CheckBox)findViewById(R.id.checkBoxVibrateWhenRinging);
spinnerSoundMode = (Spinner)findViewById(R.id.spinnerSoundMode);
spinnerDndMode = (Spinner)findViewById(R.id.spinnerDndMode);
seekBarVolumeMusic = (SeekBar)findViewById(R.id.seekBarVolumeMusic);
seekBarVolumeNotifications = (SeekBar)findViewById(R.id.seekBarVolumeNotifications);
seekBarVolumeAlarms = (SeekBar)findViewById(R.id.seekBarVolumeAlarms);
@ -114,6 +118,7 @@ public class ActivityManageProfile extends Activity
checkBoxScreenLockUnlockSound.setEnabled(false);
checkBoxHapticFeedback.setEnabled(false);
spinnerSoundMode.setEnabled(false);
spinnerDndMode.setEnabled(false);
seekBarVolumeMusic.setEnabled(false);
seekBarVolumeNotifications.setEnabled(false);
seekBarVolumeAlarms.setEnabled(false);
@ -121,6 +126,14 @@ public class ActivityManageProfile extends Activity
bChangeSoundNotifications.setEnabled(false);
spinnerSoundMode.setSelection(0);
spinnerDndMode.setSelection(0);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
{
// Disable DND controls
checkBoxChangeDnd.setEnabled(false);
spinnerDndMode.setEnabled(false);
}
// Scale SeekBars to the system's maximum volume values
AudioManager am = (AudioManager) Miscellaneous.getAnyContext().getSystemService(Context.AUDIO_SERVICE);
@ -128,8 +141,30 @@ public class ActivityManageProfile extends Activity
seekBarVolumeNotifications.setMax(am.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION));
seekBarVolumeAlarms.setMax(am.getStreamMaxVolume(AudioManager.STREAM_ALARM));
soundModeAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, new String[] { getResources().getString(R.string.soundModeSilent), getResources().getString(R.string.soundModeVibrate), getResources().getString(R.string.soundModeNormal) });
soundModeAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, new String[]
{
getResources().getString(R.string.soundModeSilent),
getResources().getString(R.string.soundModeVibrate),
getResources().getString(R.string.soundModeNormal)
});
spinnerSoundMode.setAdapter(soundModeAdapter);
dndModeAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, new String[]
{
getResources().getString(R.string.dndOff),
getResources().getString(R.string.dndPriority),
getResources().getString(R.string.dndNothing),
getResources().getString(R.string.dndAlarms)
});
spinnerDndMode.setAdapter(dndModeAdapter);
/*
Order in spinner: 1, 2, 4, 3
NotificationManager.INTERRUPTION_FILTER_UNKNOWN -> Returned when the value is unavailable for any reason.
NotificationManager.INTERRUPTION_FILTER_ALL -> 1 -> Normal interruption filter - no notifications are suppressed. -> essentially turn off DND
NotificationManager.INTERRUPTION_FILTER_PRIORITY -> 2 -> Priority interruption filter - all notifications are suppressed except those that match the priority criteria.
NotificationManager.INTERRUPTION_FILTER_ALARMS -> 4 -> Alarms only interruption filter - all notifications except those of category
NotificationManager.INTERRUPTION_FILTER_NONE -> 3 -> No interruptions filter - all notifications are suppressed and all audio streams (except those used for phone calls) and vibrations are muted.
*/
checkBoxChangeSoundMode.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
@ -139,6 +174,14 @@ public class ActivityManageProfile extends Activity
spinnerSoundMode.setEnabled(isChecked);
}
});
checkBoxChangeDnd.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
spinnerDndMode.setEnabled(isChecked);
}
});
checkBoxChangeVolumeMusicVideoGameMedia.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
@Override
@ -327,6 +370,7 @@ public class ActivityManageProfile extends Activity
{
etName.setText(ActivityMainProfiles.profileToEdit.getName());
checkBoxChangeSoundMode.setChecked(ActivityMainProfiles.profileToEdit.getChangeSoundMode());
checkBoxChangeDnd.setChecked(ActivityMainProfiles.profileToEdit.getChangeDndMode());
checkBoxChangeVolumeMusicVideoGameMedia.setChecked(ActivityMainProfiles.profileToEdit.getChangeVolumeMusicVideoGameMedia());
checkBoxChangeVolumeNotifications.setChecked(ActivityMainProfiles.profileToEdit.getChangeVolumeNotifications());
checkBoxChangeVolumeAlarms.setChecked(ActivityMainProfiles.profileToEdit.getChangeVolumeAlarms());
@ -338,6 +382,7 @@ public class ActivityManageProfile extends Activity
checkBoxChangeVibrateWhenRinging.setChecked(ActivityMainProfiles.profileToEdit.getChangeVibrateWhenRinging());
spinnerSoundMode.setSelection(ActivityMainProfiles.profileToEdit.getSoundMode());
spinnerDndMode.setSelection(ActivityMainProfiles.profileToEdit.getDndMode()-1);
seekBarVolumeMusic.setProgress(ActivityMainProfiles.profileToEdit.getVolumeMusic());
seekBarVolumeNotifications.setProgress(ActivityMainProfiles.profileToEdit.getVolumeNotifications());
seekBarVolumeAlarms.setProgress(ActivityMainProfiles.profileToEdit.getVolumeAlarms());
@ -359,6 +404,7 @@ public class ActivityManageProfile extends Activity
ActivityMainProfiles.profileToEdit.setName(etName.getText().toString());
ActivityMainProfiles.profileToEdit.setChangeSoundMode(checkBoxChangeSoundMode.isChecked());
ActivityMainProfiles.profileToEdit.setChangeDndMode(checkBoxChangeDnd.isChecked());
ActivityMainProfiles.profileToEdit.setChangeVolumeMusicVideoGameMedia(checkBoxChangeVolumeMusicVideoGameMedia.isChecked());
ActivityMainProfiles.profileToEdit.setChangeVolumeNotifications(checkBoxChangeVolumeNotifications.isChecked());
ActivityMainProfiles.profileToEdit.setChangeVolumeAlarms(checkBoxChangeVolumeAlarms.isChecked());
@ -374,6 +420,7 @@ public class ActivityManageProfile extends Activity
ActivityMainProfiles.profileToEdit.setHapticFeedback(checkBoxHapticFeedback.isChecked());
ActivityMainProfiles.profileToEdit.setVibrateWhenRinging(checkBoxVibrateWhenRinging.isChecked());
ActivityMainProfiles.profileToEdit.setSoundMode(spinnerSoundMode.getSelectedItemPosition());
ActivityMainProfiles.profileToEdit.setDndMode(spinnerDndMode.getSelectedItemPosition()+1);
ActivityMainProfiles.profileToEdit.setVolumeMusic(seekBarVolumeMusic.getProgress());
ActivityMainProfiles.profileToEdit.setVolumeNotifications(seekBarVolumeNotifications.getProgress());
ActivityMainProfiles.profileToEdit.setVolumeAlarms(seekBarVolumeAlarms.getProgress());
@ -401,21 +448,23 @@ public class ActivityManageProfile extends Activity
}
if(!checkBoxChangeSoundMode.isChecked()
&
&&
!checkBoxChangeDnd.isChecked()
&&
!checkBoxChangeVolumeMusicVideoGameMedia.isChecked()
&
&&
!checkBoxChangeVolumeNotifications.isChecked()
&
&&
!checkBoxChangeVolumeAlarms.isChecked()
&
&&
!checkBoxChangeIncomingCallsRingtone.isChecked()
&
&&
!checkBoxChangeNotificationRingtone.isChecked()
&
&&
!checkBoxChangeAudibleSelection.isChecked()
&
&&
!checkBoxChangeScreenLockUnlockSound.isChecked()
&
&&
!checkBoxChangeHapticFeedback.isChecked()
)
{

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,297 @@
package com.jens.automation2;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.text.InputFilter;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.jens.automation2.receivers.DeviceOrientationListener;
import org.apache.commons.lang3.StringUtils;
public class ActivityManageTriggerDeviceOrientation extends Activity
{
TextView currentAzimuth, currentPitch, currentRoll, tvAppliesAzimuth, tvAppliesPitch, tvAppliesRoll;
Button bApplyPositionValues, bSavePositionValues;
EditText etDesiredAzimuth, etDesiredAzimuthTolerance, etDesiredPitch, etDesiredPitchTolerance, etDesiredRoll, etDesiredRollTolerance;
CheckBox chkDevicePositionApplies;
public static String vectorFieldName = "deviceVector";
boolean editMode = false;
float desiredAzimuth, desiredPitch, desiredRoll, desiredAzimuthTolerance, desiredPitchTolerance, desiredRollTolerance;
public void updateFields(float azimuth, float pitch, float roll)
{
currentAzimuth.setText(Float.toString(azimuth));
currentPitch.setText(Float.toString(pitch));
currentRoll.setText(Float.toString(roll));
try
{
desiredAzimuth = Float.parseFloat(etDesiredAzimuth.getText().toString());
desiredAzimuthTolerance = Float.parseFloat(etDesiredAzimuthTolerance.getText().toString());
if (desiredAzimuthTolerance == 180 || (desiredAzimuth - desiredAzimuthTolerance <= azimuth && azimuth <= desiredAzimuth + desiredAzimuthTolerance))
{
tvAppliesAzimuth.setText(getResources().getString(R.string.yes));
tvAppliesAzimuth.setTextColor(Color.GREEN);
}
else
{
tvAppliesAzimuth.setText(getResources().getString(R.string.no));
tvAppliesAzimuth.setTextColor(Color.RED);
}
}
catch(Exception e)
{
tvAppliesAzimuth.setText("");
}
try
{
desiredPitch = Float.parseFloat(etDesiredPitch.getText().toString());
desiredPitchTolerance = Float.parseFloat(etDesiredPitchTolerance.getText().toString());
if (desiredPitchTolerance == 180 || (desiredPitch - desiredPitchTolerance <= pitch && pitch <= desiredPitch + desiredPitchTolerance))
{
tvAppliesPitch.setText(getResources().getString(R.string.yes));
tvAppliesPitch.setTextColor(Color.GREEN);
}
else
{
tvAppliesPitch.setText(getResources().getString(R.string.no));
tvAppliesPitch.setTextColor(Color.RED);
}
}
catch(Exception e)
{
tvAppliesPitch.setText("");
}
try
{
desiredRoll = Float.parseFloat(etDesiredRoll.getText().toString());
desiredRollTolerance = Float.parseFloat(etDesiredRollTolerance.getText().toString());
if (desiredRollTolerance == 180 || (desiredRoll - desiredRollTolerance <= roll && roll <= desiredRoll + desiredRollTolerance))
{
tvAppliesRoll.setText(getResources().getString(R.string.yes));
tvAppliesRoll.setTextColor(Color.GREEN);
}
else
{
tvAppliesRoll.setText(getResources().getString(R.string.no));
tvAppliesRoll.setTextColor(Color.RED);
}
}
catch(Exception e)
{
tvAppliesRoll.setText("");
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_trigger_device_orientation);
currentAzimuth = (TextView) findViewById(R.id.tvCurrentAzimuth);
currentPitch = (TextView) findViewById(R.id.tvCurrentOrientationPitch);
currentRoll = (TextView) findViewById(R.id.tvCurrentRoll);
tvAppliesAzimuth = (TextView) findViewById(R.id.tvAppliesAzimuth);
tvAppliesPitch = (TextView) findViewById(R.id.tvAppliesPitch);
tvAppliesRoll = (TextView) findViewById(R.id.tvAppliesRoll);
bApplyPositionValues = (Button) findViewById(R.id.bApplyPositionValues);
bSavePositionValues = (Button) findViewById(R.id.bSavePositionValues);
etDesiredAzimuth = (EditText) findViewById(R.id.etDesiredAzimuth);
etDesiredAzimuthTolerance = (EditText) findViewById(R.id.etDesiredAzimuthTolerance);
etDesiredPitch = (EditText) findViewById(R.id.etDesiredPitch);
etDesiredPitchTolerance = (EditText) findViewById(R.id.etDesiredPitchTolerance);
etDesiredRoll = (EditText) findViewById(R.id.etDesiredRoll);
etDesiredRollTolerance = (EditText) findViewById(R.id.etDesiredRollTolerance);
chkDevicePositionApplies = (CheckBox)findViewById(R.id.chkDevicePositionApplies);
// etDesiredAzimuth.setFilters(new InputFilter[]{new InputFilterMinMax(-180, 180)});
// etDesiredPitch.setFilters(new InputFilter[]{new InputFilterMinMax(-180, 180)});
// etDesiredRoll.setFilters(new InputFilter[]{new InputFilterMinMax(-180, 180)});
etDesiredAzimuthTolerance.setFilters(new InputFilter[]{new InputFilterMinMax(0, 180)});
etDesiredPitchTolerance.setFilters(new InputFilter[]{new InputFilterMinMax(0, 180)});
etDesiredRollTolerance.setFilters(new InputFilter[]{new InputFilterMinMax(0, 180)});
if(getIntent().hasExtra(vectorFieldName))
{
editMode = true;
try
{
boolean chkValue = getIntent().getBooleanExtra(ActivityManageRule.intentNameTriggerParameter1, true);
chkDevicePositionApplies.setChecked(chkValue);
String values[] = getIntent().getStringExtra(vectorFieldName).split(Trigger.triggerParameter2Split);
etDesiredAzimuth.setText(values[0]);
etDesiredAzimuthTolerance.setText(values[1]);
etDesiredPitch.setText(values[2]);
etDesiredPitchTolerance.setText(values[3]);
etDesiredRoll.setText(values[4]);
etDesiredRollTolerance.setText(values[5]);
}
catch(Exception e)
{
Toast.makeText(ActivityManageTriggerDeviceOrientation.this, getResources().getString(R.string.triggerWrong), Toast.LENGTH_SHORT).show();
Miscellaneous.logEvent("e", "DevicePositionTrigger", "There\'s something wrong with a device position trigger. Content: " + getIntent().getStringExtra(vectorFieldName) + ", " + Log.getStackTraceString(e), 1);
}
}
bApplyPositionValues.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
// Round the values. Too long decimals will destroy the layout
if(!StringUtils.isEmpty(currentAzimuth.getText()))
etDesiredAzimuth.setText(String.valueOf(Math.round(Float.parseFloat(currentAzimuth.getText().toString()))));
if(!StringUtils.isEmpty(currentPitch.getText()))
etDesiredPitch.setText(String.valueOf(Math.round(Float.parseFloat(currentPitch.getText().toString()))));
if(!StringUtils.isEmpty(currentRoll.getText()))
etDesiredRoll.setText(String.valueOf(Math.round(Float.parseFloat(currentRoll.getText().toString()))));
}
});
bSavePositionValues.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if(!checkInputs(true))
{
Toast.makeText(ActivityManageTriggerDeviceOrientation.this, getResources().getString(R.string.enterValidNumbersIntoAllFields), Toast.LENGTH_LONG).show();
}
else
{
// Save
Intent returnData = new Intent();
returnData.putExtra(ActivityManageRule.intentNameTriggerParameter1, chkDevicePositionApplies.isChecked());
returnData.putExtra(vectorFieldName,
etDesiredAzimuth.getText().toString() + Trigger.triggerParameter2Split +
etDesiredAzimuthTolerance.getText().toString() + Trigger.triggerParameter2Split +
etDesiredPitch.getText().toString() + Trigger.triggerParameter2Split +
etDesiredPitchTolerance.getText().toString() + Trigger.triggerParameter2Split +
etDesiredRoll.getText().toString() + Trigger.triggerParameter2Split +
etDesiredRollTolerance.getText().toString());
setResult(RESULT_OK, returnData);
finish();
}
}
});
}
boolean checkInputs(boolean showMessages)
{
if(
!StringUtils.isEmpty(etDesiredAzimuth.getText().toString()) && Miscellaneous.isNumeric(etDesiredAzimuth.getText().toString())
&&
!StringUtils.isEmpty(etDesiredAzimuthTolerance.getText().toString()) && Miscellaneous.isNumeric(etDesiredAzimuthTolerance.getText().toString())
&&
!StringUtils.isEmpty(etDesiredPitch.getText().toString()) && Miscellaneous.isNumeric(etDesiredPitch.getText().toString())
&&
!StringUtils.isEmpty(etDesiredPitchTolerance.getText().toString()) && Miscellaneous.isNumeric(etDesiredPitchTolerance.getText().toString())
&&
!StringUtils.isEmpty(etDesiredRoll.getText().toString()) && Miscellaneous.isNumeric(etDesiredRoll.getText().toString())
&&
!StringUtils.isEmpty(etDesiredRollTolerance.getText().toString()) && Miscellaneous.isNumeric(etDesiredRollTolerance.getText().toString())
)
{
float da = Float.parseFloat(etDesiredAzimuth.getText().toString());
float dp = Float.parseFloat(etDesiredPitch.getText().toString());
float dr = Float.parseFloat(etDesiredRoll.getText().toString());
if(Math.abs(da) > 180 || Math.abs(dp) > 180 || Math.abs(dr) > 180)
{
return false;
}
if(showMessages)
{
float dat = Float.parseFloat(etDesiredAzimuthTolerance.getText().toString());
float dpt = Float.parseFloat(etDesiredPitchTolerance.getText().toString());
float drt = Float.parseFloat(etDesiredRollTolerance.getText().toString());
/*
The user may enter a tolerance of 180° for two directions, but not all three.
Otherwise this trigger would always apply.
*/
if (Math.abs(dat) >= 180 && Math.abs(dpt) >= 180 && Math.abs(drt) >= 180)
{
Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.toleranceOf180OnlyAllowedIn2Fields), ActivityManageTriggerDeviceOrientation.this).show();
return false;
}
}
return true;
}
return false;
}
@Override
protected void onResume()
{
super.onResume();
DeviceOrientationListener.getInstance().startSensorFromConfigActivity(ActivityManageTriggerDeviceOrientation.this, this);
}
@Override
protected void onPause()
{
super.onPause();
DeviceOrientationListener.getInstance().stopSensorFromConfigActivity();
}
public class InputFilterMinMax implements InputFilter
{
private float minimumValue;
private float maximumValue;
public InputFilterMinMax(float minimumValue, float maximumValue)
{
this.minimumValue = minimumValue;
this.maximumValue = maximumValue;
}
private boolean isInRange(float a, float b, float c)
{
return b > a ? c >= a && c <= b : c >= b && c <= a;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
{
try
{
int input = Integer.parseInt(dest.subSequence(0, dstart).toString() + source + dest.subSequence(dend, dest.length()));
if (isInRange(minimumValue, maximumValue, input))
return null;
}
catch (NumberFormatException nfe)
{
}
return "";
}
}
}

View File

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

View File

@ -0,0 +1,405 @@
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 final String intentNameNotificationApp = "app";
public static final String intentNameNotificationTitleDir = "titleDir";
public static final String intentNameNotificationTitle = "title";
public static final String intentNameNotificationTextDir = "textDir";
public static final String intentNameNotificationText = "text";
public static final String intentNameNotificationDirection = "direction";
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(intentNameNotificationDirection, chkNotificationDirection.isChecked());
data.putExtra(intentNameNotificationApp, app);
data.putExtra(intentNameNotificationTitleDir, titleDir);
data.putExtra(intentNameNotificationTitle, title);
data.putExtra(intentNameNotificationTextDir, textDir);
data.putExtra(intentNameNotificationText, 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

@ -0,0 +1,117 @@
package com.jens.automation2;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.util.List;
public class ActivityManageTriggerProfile extends Activity
{
public static final String profileFieldName = "profileName";
boolean editMode = false;
Button bSaveTriggerProfile;
Spinner spinnerProfiles;
CheckBox chkProfileActive, chkProfileCheckSettings;
ArrayAdapter<Profile> profileSpinnerAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_trigger_profile);
bSaveTriggerProfile = (Button)findViewById(R.id.bSaveTriggerProfile);
spinnerProfiles = (Spinner)findViewById(R.id.spinnerProfiles);
chkProfileActive = (CheckBox)findViewById(R.id.chkProfileActive);
chkProfileCheckSettings = (CheckBox)findViewById(R.id.chkProfileCheckSettings);
try
{
profileSpinnerAdapter = new ArrayAdapter<Profile>(this, R.layout.text_view_for_poi_listview_mediumtextsize, Profile.getProfileCollection());
loadProfileItems();
}
catch (Exception e)
{
Miscellaneous.logEvent("w", "ActivityManageTriggerProfile", Log.getStackTraceString(e), 1);
}
if(getIntent().hasExtra(ActivityManageRule.intentNameTriggerParameter2))
{
editMode = true;
boolean active = getIntent().getBooleanExtra(ActivityManageRule.intentNameTriggerParameter1, true);
chkProfileActive.setChecked(active);
try
{
String values[] = getIntent().getStringExtra(ActivityManageRule.intentNameTriggerParameter2).split(Trigger.triggerParameter2Split);
if(values.length >= 2)
{
boolean checkSettings = Boolean.parseBoolean(values[0]);
chkProfileCheckSettings.setChecked(checkSettings);
String profileName = values[0];
List<Profile> profileList = Profile.getProfileCollection();
for(int i = 0; i < profileList.size(); i++)
{
if(profileList.get(i).getName().equals(profileName))
{
spinnerProfiles.setSelection(i);
break;
}
}
}
}
catch(Exception e)
{
Toast.makeText(ActivityManageTriggerProfile.this, getResources().getString(R.string.triggerWrong), Toast.LENGTH_SHORT).show();
Miscellaneous.logEvent("e", "ActivityManageTriggerProfile", "There\'s something wrong with parameters. Content: " + getIntent().getStringExtra(ActivityManageRule.intentNameActionParameter2) + ", " + Log.getStackTraceString(e), 1);
}
}
bSaveTriggerProfile.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent returnData = new Intent();
returnData.putExtra(ActivityManageRule.intentNameTriggerParameter1, chkProfileActive.isChecked());
returnData.putExtra(ActivityManageRule.intentNameTriggerParameter2,
spinnerProfiles.getSelectedItem().toString() + Trigger.triggerParameter2Split +
chkProfileCheckSettings.isChecked());
setResult(RESULT_OK, returnData);
finish();
}
});
}
private void loadProfileItems()
{
try
{
if(spinnerProfiles.getAdapter() == null)
spinnerProfiles.setAdapter(profileSpinnerAdapter);
profileSpinnerAdapter.notifyDataSetChanged();
}
catch(NullPointerException e)
{
e.printStackTrace();
}
}
}

View File

@ -6,20 +6,26 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TimePicker;
import android.widget.Toast;
import org.apache.commons.lang3.StringUtils;
import java.sql.Time;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
public class ActivityManageTimeFrame extends Activity
public class ActivityManageTriggerTimeFrame extends Activity
{
Button bSaveTimeFrame;
TimePicker startPicker, stopPicker;
CheckBox checkMonday, checkTuesday, checkWednesday, checkThursday, checkFriday, checkSaturday, checkSunday;
CheckBox checkMonday, checkTuesday, checkWednesday, checkThursday, checkFriday, checkSaturday, checkSunday, chkRepeat;
RadioButton radioTimeFrameEntering, radioTimeFrameLeaving;
EditText etRepeatEvery;
public static Trigger editedTimeFrameTrigger = null;
@ -27,7 +33,7 @@ public class ActivityManageTimeFrame extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.trigger_timeframe_editor);
setContentView(R.layout.activity_manage_trigger_timeframe);
startPicker = (TimePicker)findViewById(R.id.tpTimeFrameStart);
stopPicker = (TimePicker)findViewById(R.id.tpTimeFrameStop);
@ -44,7 +50,9 @@ public class ActivityManageTimeFrame extends Activity
checkSunday = (CheckBox)findViewById(R.id.checkSunday);
radioTimeFrameEntering = (RadioButton)findViewById(R.id.radioTimeFrameEntering);
radioTimeFrameLeaving = (RadioButton)findViewById(R.id.radioTimeFrameLeaving);
chkRepeat = (CheckBox)findViewById(R.id.chkRepeat);
etRepeatEvery = (EditText)findViewById(R.id.etRepeatEvery);
bSaveTimeFrame.setOnClickListener(new OnClickListener()
{
@Override
@ -92,11 +100,43 @@ public class ActivityManageTimeFrame extends Activity
{
Toast.makeText(getBaseContext(), getResources().getString(R.string.selectOneDay), Toast.LENGTH_LONG).show();
return;
}
}
boolean goOn = false;
if(chkRepeat.isChecked())
{
if(!StringUtils.isEmpty(etRepeatEvery.getText().toString()))
{
try
{
long value = Long.parseLong(etRepeatEvery.getText().toString());
if(value > 0)
{
goOn = true;
}
}
catch(Exception e)
{
}
}
}
else
goOn = true;
if(!goOn)
{
Toast.makeText(getBaseContext(), getResources().getString(R.string.enterRepetitionTime), Toast.LENGTH_LONG).show();
return;
}
if(editedTimeFrameTrigger.getTimeFrame() == null)
{
// add new one
editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList));
if(chkRepeat.isChecked())
editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList, Long.parseLong(etRepeatEvery.getText().toString())));
else
editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList, 0));
}
else
{
// edit one
@ -104,6 +144,11 @@ public class ActivityManageTimeFrame extends Activity
editedTimeFrameTrigger.getTimeFrame().setTriggerTimeStop(stopTime);
editedTimeFrameTrigger.getTimeFrame().getDayList().clear();
editedTimeFrameTrigger.getTimeFrame().setDayList(dayList);
if(chkRepeat.isChecked())
editedTimeFrameTrigger.getTimeFrame().setRepetition(Long.parseLong(etRepeatEvery.getText().toString()));
else
editedTimeFrameTrigger.getTimeFrame().setRepetition(0);
}
editedTimeFrameTrigger.setTriggerParameter(radioTimeFrameEntering.isChecked());
@ -112,6 +157,15 @@ public class ActivityManageTimeFrame extends Activity
finish();
}
});
chkRepeat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
etRepeatEvery.setEnabled(isChecked);
}
});
if(editedTimeFrameTrigger.getTimeFrame() != null)
loadVariableIntoGui();
@ -158,6 +212,12 @@ public class ActivityManageTimeFrame extends Activity
break;
}
}
if(editedTimeFrameTrigger.getTimeFrame().getRepetition() > 0)
{
chkRepeat.setChecked(true);
etRepeatEvery.setText(String.valueOf(editedTimeFrameTrigger.getTimeFrame().getRepetition()));
}
}
}

View File

@ -0,0 +1,196 @@
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.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
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
{
public final static String intentNameWifiState = "wifiState";
public final static String intentNameWifiName = "wifiName";
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);
spinnerWifiList.setEnabled(false); // bug in Android; this only works when done in code, not in xml
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()
{
if(Build.VERSION.SDK_INT >= 30)
{
Miscellaneous.messageBox(getResources().getString(R.string.hint), getResources().getString(R.string.wifiApi30), ActivityManageTriggerWifi.this).show();
loadListOfVisibleWifis();
}
else
{
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();
}
void loadListOfVisibleWifis()
{
List<ScanResult> results = null;
try
{
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
results = wifiManager.getScanResults();
for (ScanResult wifi : results)
wifiList.add(wifi.SSID.replaceAll("\"+$", "").replaceAll("^\"+", ""));
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "loadListOfVisibleWifis()", Log.getStackTraceString(e), 1);
}
}
@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;
}
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -30,14 +30,14 @@ public class ActivityVolumeTest extends Activity
instance = this;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_volume_test);
setContentView(R.layout.activity_volume_calibration);
tvCurrentVolume = (TextView)findViewById(R.id.tvCurrentVolume);
etReferenceValue = (EditText)findViewById(R.id.etReferenceValue);
sbReferenceValue = (SeekBar)findViewById(R.id.sbReferenceValue);
tvVolumeTestExplanation = (TextView)findViewById(R.id.tvVolumeTestExplanation);
tvVolumeTestExplanation = (TextView)findViewById(R.id.tvVolumeCalibrationExplanation);
tvVolumeTestExplanation.setText(String.format(getResources().getString(R.string.volumeTesterExplanation), String.valueOf(volumeRefreshInterval)));
tvVolumeTestExplanation.setText(String.format(getResources().getString(R.string.volumeCalibrationExplanation), String.valueOf(volumeRefreshInterval)));
etReferenceValue.setText(String.valueOf(Settings.referenceValueForNoiseLevelMeasurements));

View File

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

View File

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
@ -23,6 +24,7 @@ import android.util.Log;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.jens.automation2.Trigger.Trigger_Enum;
import com.jens.automation2.location.LocationProvider;
@ -37,9 +39,10 @@ public class AutomationService extends Service implements OnInitListener
protected TextToSpeech ttsEngine = null;
protected final static int notificationId = 1000;
protected final static int notificationIdRestrictions = 1005;
protected final static int notificationIdLocationRestriction = 1006;
final static String NOTIFICATION_CHANNEL_ID = "com.jens.automation2";
final static String channelName = "Automation notifications";
final static String channelName = "Service notification";
protected static Notification myNotification;
protected static NotificationCompat.Builder notificationBuilder = null;
@ -120,14 +123,14 @@ public class AutomationService extends Service implements OnInitListener
if(Build.VERSION.SDK_INT >= 28)
{
if (!ActivityPermissions.havePermission(ActivityPermissions.permissionNameStartService, AutomationService.this))
if (!ActivityPermissions.havePermission(Manifest.permission.FOREGROUND_SERVICE, AutomationService.this))
{
/*
Don't have permission to start service. This is a show stopper.
*/
Miscellaneous.logEvent("e", "Permission", "Don't have permission to start foreground service. Will request it now.", 4);
// Toast.makeText(AutomationService.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
ActivityPermissions.requestSpecificPermission(ActivityPermissions.permissionNameStartService);
ActivityPermissions.requestSpecificPermission(Manifest.permission.FOREGROUND_SERVICE);
return false;
}
}
@ -154,7 +157,8 @@ public class AutomationService extends Service implements OnInitListener
{
Miscellaneous.logEvent("w", "AutoStart", "Service is started via boot. Settingsfile not available because storage is not mounted, yet. Waiting for 3 seconds.", 4);
Thread.sleep(3000);
} catch (InterruptedException e)
}
catch (InterruptedException e)
{
e.printStackTrace();
}
@ -169,15 +173,13 @@ public class AutomationService extends Service implements OnInitListener
}
//if still no POIs...
if (//PointOfInterest.getPointOfInterestCollection() == null | PointOfInterest.getPointOfInterestCollection().size() == 0
// &&
Rule.getRuleCollection() == null | Rule.getRuleCollection().size() == 0
)
if (Rule.getRuleCollection() == null | Rule.getRuleCollection().size() == 0)
{
Miscellaneous.logEvent("w", "AutomationService", context.getResources().getString(R.string.serviceWontStart), 1);
Toast.makeText(context, context.getResources().getString(R.string.serviceWontStart), Toast.LENGTH_LONG).show();
return false;
} else
}
else
{
return true;
}
@ -196,7 +198,8 @@ public class AutomationService extends Service implements OnInitListener
if (checkStartupRequirements(this, startAtBoot))
{
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.logServiceStarting), 1);
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.logServiceStarting) + " VERSION_CODE: " + BuildConfig.VERSION_CODE + ", VERSION_NAME: " + BuildConfig.VERSION_NAME + ", flavor: " + BuildConfig.FLAVOR, 1);
Miscellaneous.logEvent("i", "Service", ActivityMaintenance.getSystemInfo(), 1);
startUpRoutine();
@ -210,7 +213,7 @@ public class AutomationService extends Service implements OnInitListener
ActivityMainScreen.updateMainScreen();
this.isRunning = true;
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.serviceStarted) + " " + String.format(this.getResources().getString(R.string.version), BuildConfig.VERSION_NAME + "(Build " + BuildConfig.VERSION_CODE + ")"), 1);
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.serviceStarted) + " VERSION_CODE: " + BuildConfig.VERSION_CODE + ", VERSION_NAME: " + BuildConfig.VERSION_NAME + ", flavor: " + BuildConfig.FLAVOR, 1);
Toast.makeText(this, this.getResources().getString(R.string.serviceStarted), Toast.LENGTH_LONG).show();
// ********** Test area **********
// Miscellaneous.logEvent("i", "setNetworkType", "bin hier.", 3);
@ -257,7 +260,8 @@ public class AutomationService extends Service implements OnInitListener
case reloadSettings:
Settings.readFromPersistentStorage(this);
applySettingsAndRules();
myLocationProvider.applySettingsAndRules();
if(myLocationProvider != null)
myLocationProvider.applySettingsAndRules();
break;
case updateNotification:
this.updateNotification();
@ -270,16 +274,7 @@ public class AutomationService extends Service implements OnInitListener
public void applySettingsAndRules()
{
if (Settings.useTextToSpeechOnNormal | Settings.useTextToSpeechOnSilent | Settings.useTextToSpeechOnVibrate)
{
if (ttsEngine == null)
ttsEngine = new TextToSpeech(this, this);
}
else
{
if (ttsEngine != null)
ttsEngine.shutdown();
}
checkForTtsEngine();
startLocationProvider();
ReceiverCoordinator.startAllReceivers();
@ -302,7 +297,7 @@ public class AutomationService extends Service implements OnInitListener
public void checkForTtsEngine()
{
if (Settings.useTextToSpeechOnNormal | Settings.useTextToSpeechOnSilent | Settings.useTextToSpeechOnVibrate | Rule.isAnyRuleUsing(Action.Action_Enum.speakText))
if (Settings.useTextToSpeechOnNormal || Settings.useTextToSpeechOnSilent || Settings.useTextToSpeechOnVibrate || Rule.isAnyRuleUsing(Action.Action_Enum.speakText))
{
if (ttsEngine == null)
ttsEngine = new TextToSpeech(this, this);
@ -318,6 +313,7 @@ public class AutomationService extends Service implements OnInitListener
checkForTtsEngine();
checkForPermissions();
checkForRestrictedFeatures();
checkForMissingBackgroundLocationPermission();
Actions.context = this;
Actions.autoMationServerRef = this;
@ -330,7 +326,7 @@ public class AutomationService extends Service implements OnInitListener
protected void startLocationProvider()
{
if(ActivityPermissions.havePermission("android.permission.ACCESS_COARSE_LOCATION", AutomationService.this))
if(ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, AutomationService.this))
myLocationProvider = new LocationProvider(this); //autostart with this (only) constructor
}
@ -340,19 +336,22 @@ public class AutomationService extends Service implements OnInitListener
{
boolean displayNotification = false;
String rule = "";
outerLoop:
for(Rule r : Rule.getRuleCollection())
{
if(r.isRuleActive())
{
if(!r.haveEnoughPermissions())
// for (String permission : ActivityPermissions.getPermissionsForRule(r))
{
// if (!ActivityPermissions.havePermission(permission, AutomationService.this))
{
// r.setRuleActive(false);
// r.change(AutomationService.this);
if(!displayNotification)
{
displayNotification = true;
rule = r.getName();
break outerLoop;
}
}
}
}
@ -360,15 +359,16 @@ public class AutomationService extends Service implements OnInitListener
if(displayNotification)
{
// Toast.makeText(Miscellaneous.getAnyContext(), "Require more permissions.", Toast.LENGTH_LONG).show();
// Update notification or show new one that notifiies of the lack or permissions.
Intent intent = new Intent(AutomationService.this, ActivityPermissions.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
Miscellaneous.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 +385,38 @@ public class AutomationService extends Service implements OnInitListener
Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
// Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), ActivityPermissions.notificationIdPermissions, pi);
Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), notificationIdRestrictions, pi);
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);
}
}
}
public void cancelNotification()
{
NotificationManagerCompat.from(AutomationService.this).cancelAll();
}
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);
}
}
public static void startAutomationService(Context context, boolean startAtBoot)
{
if(!(isMyServiceRunning(context)))
@ -407,7 +434,7 @@ public class AutomationService extends Service implements OnInitListener
private void stopRoutine()
{
Log.i("STOP", "Stopping");
Miscellaneous.logEvent("i", "Service", "Stopping service...", 3);
try
{
myLocationProvider.stopLocationService();
@ -436,7 +463,6 @@ public class AutomationService extends Service implements OnInitListener
builder.setWhen(System.currentTimeMillis());
builder.setContentIntent(myPendingIntent);
// Notification defaultNotification = new Notification();
Notification defaultNotification = builder.build();
defaultNotification.icon = R.drawable.ic_launcher;
@ -454,31 +480,6 @@ public class AutomationService extends Service implements OnInitListener
// defaultNotification.ledOffMS = 1500;
return builder;
/*NotificationManager mNotificationManager = (NotificationManager) AutomationService.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder;
builder = new NotificationCompat.Builder(AutomationService.getInstance());
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
builder.setCategory(Notification.CATEGORY_EVENT);
builder.setWhen(System.currentTimeMillis());
builder.setContentTitle("Automation");
builder.setSmallIcon(R.drawable.ic_launcher);
// builder.setContentText(textToDisplay);
// builder.setSmallIcon(icon);
// builder.setContentIntent(pendingIntent);
// builder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel = new NotificationChannel("notify_001", "Channel human readable title", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
}
return builder;*/
}
protected static NotificationCompat.Builder createDefaultNotificationBuilder()
@ -528,76 +529,68 @@ public class AutomationService extends Service implements OnInitListener
if(instance != null)
{
// if(Settings.showIconWhenServiceIsRunning)
// {
Miscellaneous.logEvent("i", "Notification", "Request to update notification.", 4);
String bodyText="";
String lastRuleString = "";
if(PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0)
{
try
{
PointOfInterest activePoi = PointOfInterest.getActivePoi();
if(activePoi == null)
{
PointOfInterest closestPoi = PointOfInterest.getClosestPOI(instance.getLocationProvider().getCurrentLocation());
bodyText = "Active POI: none" + "\n" + "Closest POI: " + closestPoi.getName() + lastRuleString;
}
else
{
bodyText = "Active POI: " + activePoi.getName() + lastRuleString;
}
}
catch(NullPointerException e)
{
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, AutomationService.getInstance())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, AutomationService.getInstance())
)
bodyText = instance.getResources().getString(R.string.stillGettingPosition);
else
bodyText = instance.getResources().getString(R.string.locationEngineNotActive);
}
}
Miscellaneous.logEvent("i", "Notification", "Request to update notification.", 4);
String bodyText="";
String lastRuleString = "";
if(PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0)
{
try
{
lastRuleString = instance.getResources().getString(R.string.lastRule) + " " + Rule.getLastActivatedRule().getName() + " " + instance.getResources().getString(R.string.at) + " " + Rule.getLastActivatedRuleActivationTime().toLocaleString();
PointOfInterest activePoi = PointOfInterest.getActivePoi();
if(activePoi == null)
{
PointOfInterest closestPoi = PointOfInterest.getClosestPOI(instance.getLocationProvider().getCurrentLocation());
bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + AutomationService.getInstance().getResources().getString(R.string.none) + "\n" + AutomationService.getInstance().getResources().getString(R.string.closestPoi) + ": " + closestPoi.getName() + lastRuleString;
}
else
{
bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + activePoi.getName() + lastRuleString;
}
}
catch(Exception e)
catch(NullPointerException e)
{
lastRuleString = instance.getResources().getString(R.string.lastRule) + " n./a.";
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, AutomationService.getInstance())
&&
ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, AutomationService.getInstance())
)
bodyText = instance.getResources().getString(R.string.stillGettingPosition);
else
bodyText = instance.getResources().getString(R.string.locationEngineNotActive);
}
}
String textToDisplay = bodyText + " " + lastRuleString;
// if(Build.VERSION.SDK_INT < 11)
// {
// myNotification.setLatestEventInfo(instance, "Automation", textToDisplay, myPendingIntent);
// }
// else
// {
notificationBuilder.setContentText(textToDisplay);
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
try
{
lastRuleString = instance.getResources().getString(R.string.lastRule) + " " + Rule.getLastActivatedRule().getName() + " " + instance.getResources().getString(R.string.at) + " " + Rule.getLastActivatedRuleActivationTime().toLocaleString();
}
catch(Exception e)
{
lastRuleString = instance.getResources().getString(R.string.lastRule) + " n./a.";
}
myNotification = notificationBuilder.build();
myNotification.defaults = 0;
// }
String textToDisplay = bodyText + " " + lastRuleString;
if(notificationBuilder == null)
notificationBuilder = createDefaultNotificationBuilder();
notificationBuilder.setContentText(textToDisplay);
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
myNotification = notificationBuilder.build();
myNotification.defaults = 0;
// NotificationManager notificationManager = (NotificationManager) instance.getSystemService(NOTIFICATION_SERVICE);
// hide the notification after its selected
// hide the notification after its selected
// myNotification.flags |= Notification.FLAG_AUTO_CANCEL;
myNotification.flags |= Notification.FLAG_NO_CLEAR;
myNotification.flags |= Notification.FLAG_NO_CLEAR;
// notificationManager.notify(notificationId, myNotification);
instance.startForeground(notificationId, myNotification);
// }
// else
// instance.startForeground(notificationId, null); // do not show icon in task bar
instance.startForeground(notificationId, myNotification);
}
}
@ -621,18 +614,18 @@ public class AutomationService extends Service implements OnInitListener
**/
public void speak(String text, boolean force)
{
if(text.length() > 0 && (force | Settings.useTextToSpeechOnNormal | Settings.useTextToSpeechOnSilent | Settings.useTextToSpeechOnVibrate))
if(text.length() > 0 && (force || Settings.useTextToSpeechOnNormal || Settings.useTextToSpeechOnSilent || Settings.useTextToSpeechOnVibrate))
{
AudioManager myAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
int mode = myAudioManager.getRingerMode();
if(
(mode == AudioManager.RINGER_MODE_NORMAL && Settings.useTextToSpeechOnNormal)
|
||
(mode == AudioManager.RINGER_MODE_VIBRATE && Settings.useTextToSpeechOnVibrate)
|
||
(mode == AudioManager.RINGER_MODE_SILENT && Settings.useTextToSpeechOnSilent)
|
||
force
)
{
@ -662,12 +655,12 @@ public class AutomationService extends Service implements OnInitListener
{}
}
}
Miscellaneous.logEvent("i", "TextToSpeech", "Speaking " + text + " in language " + ttsEngine.getLanguage().toLanguageTag(), 3);
this.ttsEngine.speak(text, TextToSpeech.QUEUE_ADD, null);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "TextToSpeech", Log.getStackTraceString(e), 3);
e.printStackTrace();
}
}
}
@ -680,31 +673,6 @@ public class AutomationService extends Service implements OnInitListener
return false;
else
return true;
// boolean isActivityFound = false;
// ActivityManager activityManager = (ActivityManager)context.getSystemService (Context.ACTIVITY_SERVICE);
// List<RunningTaskInfo> activitys = activityManager.getRunningTasks(Integer.MAX_VALUE);
// isActivityFound = false;
// for (int i = 0; i < activitys.size(); i++)
// {
// if (activitys.get(i).topActivity.toString().equalsIgnoreCase("ComponentInfo{com.jens.automation/com.jens.automation.ActivityMainScreen}"))
// {
// isActivityFound = true;
// }
// }
// Miscellaneous.logEvent("i", "ActivityMainScreen", "Activity running status: " + String.valueOf(isActivityFound), 5);
// return isActivityFound;
// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// List<RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
//
// for (RunningTaskInfo task : tasks)
// {
// if (context.getPackageName().equalsIgnoreCase(task.baseActivity.getPackageName()))
// return true;
// }
//
// return false;
}
public static boolean isMyServiceRunning(Context context)
@ -716,7 +684,6 @@ public class AutomationService extends Service implements OnInitListener
{
if(AutomationService.class.getName().equals(service.service.getClassName()))
{
// return AutomationService.getInstance() != null && AutomationService.getInstance().isRunning;
return true;
}
}
@ -725,9 +692,8 @@ public class AutomationService extends Service implements OnInitListener
{
if(Log.getStackTraceString(e).contains("activate")) // Means that a poi has been activated/deactivated. Service is running.
return true;
// return AutomationService.getInstance() != null && AutomationService.getInstance().isRunning;
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,22 @@
package com.jens.automation2;
import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;
public class DeviceAdmin extends DeviceAdminReceiver
{
@Override
public void onEnabled (Context context , Intent intent)
{
super.onEnabled(context , intent) ;
Miscellaneous.logEvent("i", "DeviceAdmin", "Got permission BIND_DEVICE_ADMIN.", 3);
}
@Override
public void onDisabled (Context context , Intent intent)
{
super.onDisabled(context , intent) ;
Miscellaneous.logEvent("i", "DeviceAdmin", "Permission BIND_DEVICE_ADMIN taken.", 3);
}
}

View File

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

View File

@ -1,5 +1,6 @@
package com.jens.automation2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Notification;
@ -7,25 +8,31 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentProvider;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import org.apache.http.HttpEntity;
@ -44,14 +51,19 @@ import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.InvocationTargetException;
@ -66,10 +78,15 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.sql.Time;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@ -81,11 +98,15 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import androidx.core.app.NotificationCompat;
import androidx.documentfile.provider.DocumentFile;
import static android.provider.CalendarContract.CalendarCache.URI;
import static com.jens.automation2.AutomationService.NOTIFICATION_CHANNEL_ID;
import static com.jens.automation2.AutomationService.channelName;
public class Miscellaneous extends Service
{
{
protected static String writeableFolderStringCache = null;
public static final String lineSeparator = System.getProperty("line.separator");
@ -105,15 +126,6 @@ public class Miscellaneous extends Service
if(url.toLowerCase().contains("https"))
{
connection = (HttpsURLConnection) urlObject.openConnection();
// if(Settings.httpAcceptAllCertificates)
// {
// SSLContext sc = SSLContext.getInstance("TLS");
// sc.init(null, getInsecureTrustManager(), new java.security.SecureRandom());
// HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Miscellaneous.disableSSLCertificateChecking();
// HttpsURLConnection.setDefaultHostnameVerifier(getInsecureHostnameVerifier());
// }
}
else
connection = (HttpURLConnection) urlObject.openConnection();
@ -204,7 +216,15 @@ public class Miscellaneous extends Service
return null;
}
@Override
public static int boolToInt(boolean input)
{
if(input)
return 1;
else
return 0;
}
@Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
@ -321,7 +341,7 @@ public class Miscellaneous extends Service
migration:
if (!newConfigFile.exists())
{
if (ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, Miscellaneous.getAnyContext()))
if (ActivityPermissions.havePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Miscellaneous.getAnyContext()))
{
// We have the storage permission, probably because it's an old installation. Files should be migrated to app-specific folder.
@ -340,8 +360,8 @@ public class Miscellaneous extends Service
for (String f : foldersToTestArray)
{
if (testFolder(f))
{
// if (testFolder(f))
// {
String pathToUse = f + "/" + Settings.folderName;
// Toast.makeText(getAnyContext(), "Using " + pathToUse + " to store settings and log.", Toast.LENGTH_LONG).show();
@ -353,14 +373,24 @@ public class Miscellaneous extends Service
{
Miscellaneous.logEvent("i", "Path", "Found old path " + pathToUse + " for settings and logs. Migrating old files to new directory.", 2);
for (File moveFile : oldDirectory.listFiles())
moveFile.renameTo(newDirectory);
for (File fileToBeMoved : oldDirectory.listFiles())
{
File dstFile = new File(writeableFolderStringCache + "/" + fileToBeMoved.getName());
/*
For some stupid reason Android's file.moveTo can't move files between
mount points. That's why we have to copy it and delete the src if successful.
*/
if(copyFileUsingStream(fileToBeMoved, dstFile))
fileToBeMoved.delete();
}
String message = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.filesHaveBeenMovedTo), newDirectory.getAbsolutePath());
Miscellaneous.writeStringToFile(oldDirectory.getAbsolutePath() + "readme.txt", message);
Miscellaneous.writeStringToFile(oldDirectory.getAbsolutePath() + "/readme.txt", message);
break migration;
}
}
// }
}
} catch (Exception e)
{
@ -427,6 +457,33 @@ public class Miscellaneous extends Service
// Miscellaneous.logEvent("i", TAG, "isEmulator=" + isEmulator);
return isEmulator;
}
public static boolean compare(String direction, String needle, String haystack)
{
// If only one of needle or haystack is null
if(
(needle == null && haystack != null)
||
(needle != null && haystack == null)
)
return false;
switch(direction)
{
case Trigger.directionEquals:
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)
{
@ -495,6 +552,22 @@ public class Miscellaneous extends Service
return null;
}
public static boolean isDarkModeEnabled(Context context)
{
int mode = context.getResources().getConfiguration().uiMode;
switch(mode)
{
case 33:
case Configuration.UI_MODE_NIGHT_YES:
return true;
case 17:
case Configuration.UI_MODE_NIGHT_NO:
case Configuration.UI_MODE_NIGHT_UNDEFINED:
default:
return false;
}
}
@SuppressLint("NewApi")
public static String replaceVariablesInText(String source, Context context) throws Exception
@ -555,6 +628,32 @@ public class Miscellaneous extends Service
source = source.replace("[s]", String.valueOf(cal.get(Calendar.SECOND)));
source = source.replace("[ms]", String.valueOf(cal.get(Calendar.MILLISECOND)));
}
if(source.contains("[notificationTitle]"))
{
String notificationTitle = NotificationListener.getLastNotification().getTitle();
if(notificationTitle != null && notificationTitle.length() > 0)
source = source.replace("[notificationTitle]", notificationTitle);
else
{
source = source.replace("notificationTitle unknown", notificationTitle);
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
{
source = source.replace("notificationText unknown", notificationText);
Miscellaneous.logEvent("w", "Variable replacement", "notificationText was empty.", 3);
}
}
// Miscellaneous.logEvent("i", "URL after replace", source);
@ -600,6 +699,24 @@ public class Miscellaneous extends Service
return alertDialog.create();
}
private boolean haveNetworkConnection()
{
boolean haveConnectedWifi = false;
boolean haveConnectedMobile = false;
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo[] netInfo = cm.getAllNetworkInfo();
for (NetworkInfo ni : netInfo) {
if (ni.getTypeName().equalsIgnoreCase("WIFI"))
if (ni.isConnected())
haveConnectedWifi = true;
if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
if (ni.isConnected())
haveConnectedMobile = true;
}
return haveConnectedWifi || haveConnectedMobile;
}
/**
* Checks if the device is rooted.
@ -608,9 +725,13 @@ public class Miscellaneous extends Service
*/
public static boolean isPhoneRooted()
{
// if(true)
// return true;
// get from build info
String buildTags = Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
if (buildTags != null && buildTags.contains("test-keys"))
{
return true;
}
@ -754,65 +875,147 @@ public class Miscellaneous extends Service
return allHostsValid;
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static void createDismissableNotificationWithDelay(long delay, String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
/*
Now what's this about?
From SDK 27 onwards you can only fire 1 notification per second:
https://developer.android.com/about/versions/oreo/android-8.1?hl=bn#notify
There are some situations where the service is just being started - resulting in a notification. But we have
additional need to inform the user about something and want to create another notification. That's why we have
to delay it for a moment, but don't want to hold off the main threat.
*/
class AsyncTaskCreateNotification extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... voids)
{
setDefaultBehaviour(this);
try
{
Thread.sleep(delay);
}
catch(Exception e)
{}
createDismissableNotification(textToDisplay, notificationId, pendingIntent);
return null;
}
}
AsyncTaskCreateNotification astCn = new AsyncTaskCreateNotification();
astCn.execute(null, null);
}
private static void setDefaultBehaviour(AsyncTask asyncTask)
{
// without this line debugger will - for some reason - skip all breakpoints in this class
if(android.os.Debug.isDebuggerConnected())
android.os.Debug.waitForDebugger();
// Thread.setDefaultUncaughtExceptionHandler(Miscellaneous.getUncaughtExceptionHandler(activityMainRef, true));
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static void createDismissableNotification(String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
createDismissableNotificationSdk26(textToDisplay, notificationId, pendingIntent);
return;
}
NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder dismissableNotificationBuilder = createDismissableNotificationBuilder(pendingIntent);
dismissableNotificationBuilder.setContentText(textToDisplay);
dismissableNotificationBuilder.setContentIntent(pendingIntent);
dismissableNotificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
dismissableNotificationBuilder.setAutoCancel(true);
Notification dismissableNotification = dismissableNotificationBuilder.build();
mNotificationManager.notify(notificationId, dismissableNotification);
/*NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher) // notification icon
.setContentTitle("Notification!") // title for notification
.setContentText("Hello word") // message for notification
.setAutoCancel(true); // clear notification after click
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, dismissableNotification);*/
/*NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher) // notification icon
.setContentTitle("Notification!") // title for notification
.setContentText("Hello word") // message for notification
.setAutoCancel(true); // clear notification after click
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, dismissableNotification);*/
}
/*protected static Notification.Builder createDismissableNotificationBuilder()
static void createDismissableNotificationSdk26(String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
Notification.Builder builder = new Notification.Builder(AutomationService.getInstance());
builder.setContentTitle("Automation");
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setCategory(Notification.CATEGORY_EVENT);
NotificationManager mNotificationManager = (NotificationManager) AutomationService.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Functionality warnings", NotificationManager.IMPORTANCE_HIGH);
// chan.setLightColor(Color.BLUE);
chan.enableVibration(false);
// chan.setSound(null, null);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
mNotificationManager.createNotificationChannel(chan);
builder = new NotificationCompat.Builder(AutomationService.getInstance(), NOTIFICATION_CHANNEL_ID);
}
else
builder = new NotificationCompat.Builder(AutomationService.getInstance());
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
// builder.setCategory(Notification.CATEGORY_SERVICE);
builder.setWhen(System.currentTimeMillis());
builder.setContentIntent(pendingIntent);
//static PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0);
builder.setContentTitle(AutomationService.getInstance().getResources().getString(R.string.app_name));
builder.setOnlyAlertOnce(true);
//builder.setContentIntent(myPendingIntent);
if(Settings.showIconWhenServiceIsRunning)
builder.setSmallIcon(R.drawable.ic_launcher);
// Notification defaultNotification = new Notification();
*//* Notification defaultNotification = builder.build();
builder.setContentText(textToDisplay);
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
defaultNotification.icon = R.drawable.ic_launcher;
defaultNotification.when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, builder.build());
// defaultNotification.defaults |= Notification.DEFAULT_VIBRATE;
// defaultNotification.defaults |= Notification.DEFAULT_LIGHTS;
defaultNotification.flags |= Notification.FLAG_AUTO_CANCEL;
// defaultNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
defaultNotification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
// defaultNotification.ledARGB = Color.YELLOW;
// defaultNotification.ledOnMS = 1500;
// defaultNotification.ledOffMS = 1500;
*//*
return builder;
}*/
// Intent notifyIntent = new Intent(context, notification.class);
// notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//
// pendingIntent.getIntentSender().g
//
// PendingIntent pendingIntent = PendingIntent.getActivities(context, 0,
// new Intent[]{notifyIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
//
// Notification notification = new Notification.Builder(Miscellaneous.getAnyContext())
// .setSmallIcon(android.R.drawable.ic_dialog_info)
// .setContentTitle("Automation")
// .setContentText(textToDisplay)
// .setAutoCancel(true)
// .setContentIntent(pendingIntent)
// .build();
// notification.defaults |= Notification.DEFAULT_SOUND;
// NotificationManager notificationManager =
// (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
// notificationManager.notify(1, notification);
}
protected static NotificationCompat.Builder createDismissableNotificationBuilder(PendingIntent myPendingIntent)
{
@ -822,7 +1025,7 @@ public class Miscellaneous extends Service
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW);
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_HIGH);
// chan.setLightColor(Color.BLUE);
// chan.enableVibration(false);
// chan.setSound(null, null);
@ -837,6 +1040,7 @@ public class Miscellaneous extends Service
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
builder.setCategory(Notification.CATEGORY_SERVICE);
builder.setAutoCancel(true);
builder.setWhen(System.currentTimeMillis());
builder.setContentIntent(myPendingIntent);
@ -852,13 +1056,38 @@ public class Miscellaneous extends Service
return builder;
}
public static String explode(ArrayList<String> arrayList)
public static String explode(String glue, ArrayList<String> arrayList)
{
StringBuilder builder = new StringBuilder();
for(String s : arrayList)
builder.append(s);
if(arrayList != null)
{
StringBuilder builder = new StringBuilder();
for (String s : arrayList)
builder.append(s + glue);
return builder.toString();
if (builder.length() > 0)
builder.delete(builder.length() - glue.length(), builder.length());
return builder.toString();
}
else
return "";
}
public static String explode(String glue, String[] inputArray)
{
if(inputArray != null)
{
StringBuilder builder = new StringBuilder();
for (String s : inputArray)
builder.append(s + glue);
if (builder.length() > 0)
builder.delete(builder.length() - glue.length(), builder.length());
return builder.toString();
}
else
return "";
}
public static boolean isGooglePlayInstalled(Context context)
@ -948,11 +1177,11 @@ public class Miscellaneous extends Service
}
catch (IllegalAccessException e)
{
e.printStackTrace();
Miscellaneous.logEvent("w", "runMethodReflective", Log.getStackTraceString(e),5 );
}
catch (InvocationTargetException e)
{
e.printStackTrace();
Miscellaneous.logEvent("w", "runMethodReflective", Log.getStackTraceString(e),5 );
}
return result;
@ -1056,4 +1285,312 @@ public class Miscellaneous extends Service
return null;
}
}
public static boolean copyFileUsingStream(File source, File dest) throws IOException
{
boolean returnValue = false;
InputStream is = null;
OutputStream os = null;
try
{
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
returnValue = true;
}
finally
{
is.close();
os.close();
}
return returnValue;
}
public static boolean copyDocumentFileToFile(DocumentFile src, File dst)
{
InputStream in = null;
OutputStream out = null;
String error = null;
try
{
in = Miscellaneous.getAnyContext().getContentResolver().openInputStream(src.getUri());
out = new FileOutputStream(dst);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
return true;
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return false;
// return error;
}
public static boolean copyFileToDocumentFile(File src, DocumentFile dst)
{
InputStream in = null;
OutputStream out = null;
String error = null;
try
{
in = new FileInputStream(src);
out = Miscellaneous.getAnyContext().getContentResolver().openOutputStream(dst.getUri());
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
return true;
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return false;
// return error;
}
/*public static String copyDocumentFile(String inputPath, String inputFile, Uri treeUri)
{
InputStream in = null;
OutputStream out = null;
String error = null;
DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
String extension = inputFile.substring(inputFile.lastIndexOf(".")+1,inputFile.length());
try
{
DocumentFile newFile = pickedDir.createFile("audio/"+extension, inputFile);
out = getActivity().getContentResolver().openOutputStream(newFile.getUri());
in = new FileInputStream(inputPath + inputFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return error;
}*/
public static boolean googleToBlameForLocation(boolean checkExistingRules)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
if (BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor"))
{
if(checkExistingRules)
{
if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest))
{
return true;
}
}
else
return true;
}
}
return false;
}
public static void zip(String[] _files, String zipFileName)
{
int BUFFER = 2048;
try
{
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(zipFileName);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
byte data[] = new byte[BUFFER];
for (int i = 0; i < _files.length; i++)
{
Log.v("Compress", "Adding: " + _files[i]);
FileInputStream fi = new FileInputStream(_files[i]);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(_files[i].substring(_files[i].lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1)
{
out.write(data, 0, count);
}
origin.close();
}
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void unzip(String _zipFile, String _targetLocation)
{
int BUFFER = 2048;
try
{
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null)
{
//create dir if required while unzipping
if (ze.isDirectory())
{
// dirChecker(ze.getName());
}
else
{
FileOutputStream fout = new FileOutputStream(_targetLocation + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read())
{
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
}
catch (Exception e)
{
System.out.println(e);
}
}
public static void sendEmail(Context context, String targetAddress, String subject, String message, Uri fileAttachment)
{
try
{
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{targetAddress});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
if (fileAttachment != null)
{
emailIntent.putExtra(Intent.EXTRA_STREAM, fileAttachment);
}
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
context.startActivity(Intent.createChooser(emailIntent, "Sending email..."));
}
catch (Throwable t)
{
Toast.makeText(context, "Request failed try again: "+ t.toString(), Toast.LENGTH_LONG).show();
}
}
public static boolean doesActivityExist(Intent intent, Context context)
{
return intent.resolveActivityInfo(context.getPackageManager(), 0) != null;
}
public static boolean isRegularExpression(String regex)
{
try
{
"compareString".matches(regex); //will cause expection if no valid regex
return true;
}
catch(java.util.regex.PatternSyntaxException e)
{
}
return false;
}
public static boolean comparePhoneNumbers(String number1, String number2)
{
/* To be activated when Android S SDK comes out
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.Q)
{
TelephonyManager tm = (TelephonyManager)Miscellaneous.getAnyContext().getSystemService(Context.TELEPHONY_SERVICE);
return PhoneNumberUtils.areSamePhoneNumber(number1, number2, tm.getNetworkCountryIso());
}
else*/
return PhoneNumberUtils.compare(number1, number2);
}
public static String formatDate(Date input)
{
DateFormat sdf = null;
SimpleDateFormat fallBackFormatter = new SimpleDateFormat(Settings.dateFormat);
if(sdf == null && Settings.dateFormat != null)
sdf = new SimpleDateFormat(Settings.dateFormat);
String formattedDate;
if(sdf != null)
formattedDate = sdf.format(input);
else
formattedDate = fallBackFormatter.format(input);
return formattedDate;
}
}

View File

@ -23,7 +23,7 @@ import java.util.Map;
public class News
{
Calendar publishDate;
String applicablePlattform;
String applicablePlatform;
Map<String,NewsTranslation> translations = new HashMap<>();
public static class NewsTranslation
@ -68,9 +68,15 @@ public class News
Calendar now = Calendar.getInstance();
String newsContent;
String filePath = context.getFilesDir() + "/appNews.xml";
String newsFileName = "appNews.xml";
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.newsDisplayForXDays * 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";
newsContent = Miscellaneous.downloadURL(newsUrl, null, null);
@ -80,14 +86,14 @@ public class News
{
Settings.lastNewsPolltime = now.getTimeInMillis();
Settings.writeSettings(context);
Miscellaneous.logEvent("i", "appNews.xml", "File stored to " + filePath, 5);
Miscellaneous.logEvent("i", newsFileName, "File stored to " + filePath, 5);
}
}
else
{
// Just read local cache file
newsContent = Miscellaneous.readFileToString(filePath);
Miscellaneous.logEvent("i", "appNews.xml", "Using cache to retrieve news: " + filePath, 5);
Miscellaneous.logEvent("i", newsFileName, "Using cache to retrieve news: " + filePath, 5);
}
ArrayList<News> returnList = new ArrayList<>();
@ -145,9 +151,9 @@ public class News
String publishDateString = neEl.getElementsByTagName("publishDate").item(0).getTextContent();
newsEntry.setPublishDate(Miscellaneous.calendarFromLong(Long.parseLong(publishDateString) * 1000));
newsEntry.setApplicablePlattform(neEl.getElementsByTagName("applicablePlattforms").item(0).getTextContent());
newsEntry.setApplicablePlatform(neEl.getElementsByTagName("applicablePlattforms").item(0).getTextContent());
if(newsEntry.getApplicablePlattform().equalsIgnoreCase("all") || newsEntry.getApplicablePlattform().equalsIgnoreCase(BuildConfig.FLAVOR))
if(newsEntry.getApplicablePlatform().equalsIgnoreCase("all") || newsEntry.getApplicablePlatform().equalsIgnoreCase(BuildConfig.FLAVOR))
returnList.add(newsEntry);
}
}
@ -193,14 +199,14 @@ public class News
this.publishDate = publishDate;
}
public String getApplicablePlattform()
public String getApplicablePlatform()
{
return applicablePlattform;
return applicablePlatform;
}
public void setApplicablePlattform(String applicablePlattform)
public void setApplicablePlatform(String applicablePlatform)
{
this.applicablePlattform = applicablePlattform;
this.applicablePlatform = applicablePlatform;
}
@NonNull
@ -256,7 +262,18 @@ public class News
@Override
protected void onPostExecute(ArrayList arrayList)
{
ActivityMainScreen.getActivityMainScreenInstance().processNewsResult(arrayList);
try
{
ActivityMainScreen.getActivityMainScreenInstance().processNewsResult(arrayList);
}
catch(NullPointerException e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the already downloded news, probably ActivityMainScreen isn't currently shown: " + Log.getStackTraceString(e), 2);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the already downloded news: " + Log.getStackTraceString(e), 2);
}
}
}
}

View File

@ -252,8 +252,9 @@ public class PointOfInterest implements Comparable<PointOfInterest>
Settings.writeSettings(parentService);
Miscellaneous.logEvent("i", "POI", "Reached POI " + this.getName() + ". Checking if there's a rule that applies to that.", 2);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPoi(this, true);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.pointOfInterest);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPoi(this);
if(ruleCandidates.size()==0)
{
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " not found in ANY rule.", 2);
@ -264,18 +265,22 @@ public class PointOfInterest implements Comparable<PointOfInterest>
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(parentService) && ruleCandidates.get(i).haveEnoughPermissions())
if(ruleCandidates.get(i).haveEnoughPermissions() && ruleCandidates.get(i).getsGreenLight(parentService))
{
Miscellaneous.logEvent("i", "POI", "Rule " + ruleCandidates.get(i).getName() + " applies for entering POI " + this.getName() + ".", 2);
ruleCandidates.get(i).activate(parentService, false);
}
}
}
Miscellaneous.logEvent("i", "POI", "Reached POI " + this.getName() + ". Done checking POI rules.", 2);
parentService.updateNotification();
ActivityMainScreen.updateMainScreen();
}
}
public void deactivate(AutomationService parentService)
{
if(this.isActivated())
@ -285,8 +290,9 @@ public class PointOfInterest implements Comparable<PointOfInterest>
Settings.writeSettings(parentService);
Miscellaneous.logEvent("i", "POI", "Left POI " + this.getName() + ". Checking if there's a rule that applies to that.", 2);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPoi(this, false);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.pointOfInterest);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPoi(this);
if(ruleCandidates.size()==0)
{
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " not found in ANY rule.", 2);
@ -296,7 +302,7 @@ public class PointOfInterest implements Comparable<PointOfInterest>
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " found in " + ruleCandidates.size() + " rule(s).", 2);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(parentService))
if(ruleCandidates.get(i).haveEnoughPermissions() && ruleCandidates.get(i).getsGreenLight(parentService))
{
Miscellaneous.logEvent("i", "POI", "Rule " + ruleCandidates.get(i).getName() + " applies for leaving POI " + this.getName() + ".", 2);
ruleCandidates.get(i).activate(parentService, false);
@ -491,9 +497,17 @@ public class PointOfInterest implements Comparable<PointOfInterest>
AutomationService service = AutomationService.getInstance();
if (service != null)
{
service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
try
{
service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
}
catch(Exception e)
{
// Just log the event. This should not cause an interruption in the program flow.
Miscellaneous.logEvent("e", "save POI", "Error when trying to apply settings and rules: " + Log.getStackTraceString(e), 2);
}
}
return true;
@ -530,14 +544,18 @@ public class PointOfInterest implements Comparable<PointOfInterest>
PointOfInterest.writePoisToFile();
AutomationService service = AutomationService.getInstance();
if(service != null)
try
{
service.applySettingsAndRules();
//Easiest way to check for changes in location, reset the last known location.
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
}
catch(Exception e)
{
// Just log the event. This should not cause an interruption in the program flow.
Miscellaneous.logEvent("e", "save POI", "Error when trying to apply settings and rules: " + Log.getStackTraceString(e), 2);
}
return true;
}
@ -793,6 +811,7 @@ public class PointOfInterest implements Comparable<PointOfInterest>
{
String text = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.overlapBetweenPois), otherPoi.getName(), String.valueOf(overlap));
Miscellaneous.logEvent("w", "POI", text, 2);
// Miscellaneous.messageBox("POI", text, Miscellaneous.getAnyContext()).show();
Toast.makeText(Miscellaneous.getAnyContext(), text, Toast.LENGTH_LONG).show();
return false;
}

View File

@ -1,10 +1,12 @@
package com.jens.automation2;
import android.app.NotificationManager;
import android.content.ContentValues;
import android.content.Context;
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;
@ -13,10 +15,12 @@ import com.jens.automation2.Action.Action_Enum;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Profile implements Comparable<Profile>
{
protected static ArrayList<Profile> profileCollection = new ArrayList<Profile>();
protected static List<Profile> profileCollection = new ArrayList<Profile>();
protected static List<Profile> profileActivationHistory = new ArrayList<>();
protected String name;
protected String oldName;
@ -24,6 +28,9 @@ public class Profile implements Comparable<Profile>
protected boolean changeSoundMode;
protected int soundMode;
protected boolean changeDndMode;
protected int dndMode;
boolean changeVolumeMusicVideoGameMedia;
protected int volumeMusic;
@ -81,6 +88,26 @@ public class Profile implements Comparable<Profile>
return soundMode;
}
public boolean getChangeDndMode()
{
return changeDndMode;
}
public void setChangeDndMode(boolean changeDndMode)
{
this.changeDndMode = changeDndMode;
}
public int getDndMode()
{
return dndMode;
}
public void setDndMode(int dndMode)
{
this.dndMode = dndMode;
}
public void setChangeVolumeMusicVideoGameMedia(boolean changeVolumeMusicVideoGameMedia)
{
this.changeVolumeMusicVideoGameMedia = changeVolumeMusicVideoGameMedia;
@ -243,7 +270,7 @@ public class Profile implements Comparable<Profile>
return hapticFeedback;
}
public static ArrayList<Profile> getProfileCollection()
public static List<Profile> getProfileCollection()
{
return profileCollection;
}
@ -264,18 +291,12 @@ public class Profile implements Comparable<Profile>
return null;
}
public boolean delete(AutomationService myAutomationService)
{
// TODO Auto-generated method stub
return false;
}
private boolean applyRingTone(File ringtoneFile, int ringtoneType, Context context)
{
Miscellaneous.logEvent("i", "Profile", "Request to set ringtone to " + ringtoneFile.getAbsolutePath(), 3);
if(!ringtoneFile.exists() | !ringtoneFile.canRead())
if(!ringtoneFile.exists() || !ringtoneFile.canRead())
{
String message = "Ringtone file does not exist or cannot read it: " + ringtoneFile.getAbsolutePath();
Miscellaneous.logEvent("i", "Profile", message, 3);
@ -366,11 +387,23 @@ public class Profile implements Comparable<Profile>
}
// Check if rules reference this profile
ArrayList<Rule> rulesThatReferenceMe = Rule.findRuleCandidatesByProfile(this);
ArrayList<Rule> rulesThatReferenceMe = Rule.findRuleCandidatesByActionProfile(this);
if(rulesThatReferenceMe.size() > 0)
{
for(Rule oneRule : rulesThatReferenceMe)
{
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.profileActive)
{
String[] parts = oneTrigger.getTriggerParameter2().split(Trigger.triggerParameter2Split);
parts[0] = this.name;
oneTrigger.setTriggerParameter2(Miscellaneous.explode(Trigger.triggerParameter2Split, parts));
// We don't need to save the file. This will happen anyway in PointOfInterest.writePoisToFile() below.
}
}
for(Action oneAction : oneRule.getActionSet())
{
if(oneAction.getAction() == Action_Enum.changeSoundProfile)
@ -399,19 +432,50 @@ public class Profile implements Comparable<Profile>
return false;
}
public boolean delete()
{
for(int i = 0; i< Profile.getProfileCollection().size(); i++)
public Rule isInUseByRules()
{
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.profileActive))
{
if(Profile.getProfileCollection().get(i).getName().equals(this.getName()))
for (Rule rule : Rule.findRuleCandidatesByTriggerProfile(this))
{
Profile.getProfileCollection().remove(0);
// write to file
return XmlFileInterface.writeFile();
return rule;
}
}
else if(Rule.isAnyRuleUsing(Action_Enum.changeSoundProfile))
{
for (Rule rule : Rule.findRuleCandidatesByActionProfile(this))
{
return rule;
}
}
return null;
}
public boolean delete(Context context)
{
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.profileActive))
{
for (Rule rule : Rule.findRuleCandidatesByTriggerProfile(this))
{
Toast.makeText(context, String.format(context.getResources().getString(R.string.ruleXIsUsingProfileY), rule.getName(), this.getName()), Toast.LENGTH_LONG).show();
return false;
}
}
else if(Rule.isAnyRuleUsing(Action_Enum.changeSoundProfile))
{
for (Rule rule : Rule.findRuleCandidatesByActionProfile(this))
{
Toast.makeText(context, String.format(context.getResources().getString(R.string.ruleXIsUsingProfileY), rule.getName(), this.getName()), Toast.LENGTH_LONG).show();
return false;
}
}
else
{
profileCollection.remove(this);
return XmlFileInterface.writeFile();
}
return false;
}
@ -439,6 +503,8 @@ public class Profile implements Comparable<Profile>
{
Miscellaneous.logEvent("i", "Profile " + this.getName(), String.format(context.getResources().getString(R.string.profileActivate), this.getName()), 3);
profileActivationHistory.add(this);
AutomationService.getInstance().checkLockSoundChangesTimeElapsed();
if(AutomationService.getInstance().getLockSoundChangesEnd() == null)
@ -449,6 +515,9 @@ public class Profile implements Comparable<Profile>
if(changeSoundMode)
Actions.setSound(context, soundMode);
if(changeDndMode)
Actions.setDND(context, dndMode);
if(changeVolumeMusicVideoGameMedia)
am.setStreamVolume(AudioManager.STREAM_MUSIC, volumeMusic, AudioManager.FLAG_PLAY_SOUND);
@ -464,10 +533,19 @@ public class Profile implements Comparable<Profile>
applyRingTone(incomingCallsRingtone, RingtoneManager.TYPE_RINGTONE, context);
if(changeVibrateWhenRinging)
if(vibrateWhenRinging)
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ON);
else
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
android.provider.Settings.System.putInt(context.getContentResolver(), "vibrate_when_ringing", vibrateWhenRinging?1:0);
}
else
{
if (vibrateWhenRinging)
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ON);
else
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
}
}
if(changeNotificationRingtone)
if(notificationRingtone != null)
@ -498,6 +576,20 @@ public class Profile implements Comparable<Profile>
{
Miscellaneous.logEvent("e", "Profile " + this.getName(), context.getResources().getString(R.string.errorActivatingProfile) + " " + Log.getStackTraceString(e), 1);
}
finally
{
Miscellaneous.logEvent("i", "POI", "Checking for applicable rule after profile " + this.getName() + " has been activated.", 2);
List<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.profileActive);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).haveEnoughPermissions() && ruleCandidates.get(i).getsGreenLight(AutomationService.getInstance()))
{
Miscellaneous.logEvent("i", "POI", "Rule " + ruleCandidates.get(i).getName() + " applies after " + this.getName() + " has been activated.", 2);
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
}
Miscellaneous.logEvent("i", "POI", "Done checking for applicable rule after profile " + this.getName() + " has been activated.", 2);
}
}
else
{
@ -505,6 +597,104 @@ public class Profile implements Comparable<Profile>
}
}
public boolean areMySettingsCurrentlyActive(Context context)
{
Miscellaneous.logEvent("i", "Profile " + this.getName(), "Checking if profile's settings are currently active.", 3);
try
{
AudioManager am = (AudioManager) Miscellaneous.getAnyContext().getSystemService(Context.AUDIO_SERVICE);
NotificationManager mNotificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
if(changeSoundMode)
{
if(am.getRingerMode() != soundMode)
return false;
}
if(changeDndMode && Build.VERSION.SDK_INT >= 23)
{
if(mNotificationManager.getCurrentInterruptionFilter() != dndMode)
return false;
}
if(changeVolumeMusicVideoGameMedia)
{
if(am.getStreamVolume(AudioManager.STREAM_MUSIC) != volumeMusic)
return false;
}
if(changeVolumeNotifications)
{
if(am.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != volumeNotifications)
return false;
}
if(changeVolumeAlarms)
{
if(am.getStreamVolume(AudioManager.STREAM_ALARM) != volumeAlarms)
return false;
}
// if(changeIncomingCallsRingtone)
// {
// if (incomingCallsRingtone != null)
// {
// applyRingTone(incomingCallsRingtone, RingtoneManager.TYPE_RINGTONE, context);
// }
// }
if(changeVibrateWhenRinging)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
int currentSetting = android.provider.Settings.System.getInt(context.getContentResolver(), "vibrate_when_ringing");
if(currentSetting != Miscellaneous.boolToInt(vibrateWhenRinging))
return false;
}
else
{
int currentSetting = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
if(currentSetting != Miscellaneous.boolToInt(vibrateWhenRinging))
return false;
}
}
// if(changeNotificationRingtone)
// if(notificationRingtone != null)
// applyRingTone(notificationRingtone, RingtoneManager.TYPE_NOTIFICATION, context);
if(changeScreenLockUnlockSound)
{
int currentSetting = android.provider.Settings.System.getInt(context.getContentResolver(), "lockscreen_sounds_enabled");
if(currentSetting != Miscellaneous.boolToInt(screenLockUnlockSound))
return false;
}
if(changeAudibleSelection)
{
int currentSetting = android.provider.Settings.System.getInt(context.getContentResolver(), android.provider.Settings.System.SOUND_EFFECTS_ENABLED);
if(currentSetting != Miscellaneous.boolToInt(audibleSelection))
return false;
}
if(changeHapticFeedback)
{
int currentSetting = android.provider.Settings.System.getInt(context.getContentResolver(), android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED);
if(currentSetting != Miscellaneous.boolToInt(hapticFeedback))
return false;
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "Profile " + this.getName(), "Error while checking if profile settings are currently active. " + Log.getStackTraceString(e), 1);
}
Miscellaneous.logEvent("i", "Profile " + this.getName(), "This profile's settings are currently active.", 4);
return true;
}
@Override
public String toString()
{

View File

@ -5,11 +5,12 @@ import android.util.Log;
import com.jens.automation2.location.CellLocationChangedReceiver;
import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.AlarmListener;
import com.jens.automation2.receivers.DateTimeListener;
import com.jens.automation2.receivers.AutomationListenerInterface;
import com.jens.automation2.receivers.BatteryReceiver;
import com.jens.automation2.receivers.BluetoothReceiver;
import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.DeviceOrientationListener;
import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.PhoneStatusListener;
@ -42,10 +43,11 @@ public class ReceiverCoordinator
Class adClass = Class.forName("ActivityDetectionReceiver");
allImplementers = new Class[] {
adClass,
AlarmListener.class,
DateTimeListener.class,
BatteryReceiver.class,
BluetoothReceiver.class,
ConnectivityReceiver.class,
DeviceOrientationListener.class,
HeadphoneJackListener.class,
//NfcReceiver.class,
NoiseListener.class,
@ -56,13 +58,12 @@ public class ReceiverCoordinator
}
catch (ClassNotFoundException e)
{
// e.printStackTrace();
allImplementers = new Class[] {
AlarmListener.class,
DateTimeListener.class,
BatteryReceiver.class,
BluetoothReceiver.class,
ConnectivityReceiver.class,
DeviceOrientationListener.class,
HeadphoneJackListener.class,
//NfcReceiver.class,
NoiseListener.class,
@ -144,15 +145,18 @@ public class ReceiverCoordinator
ConnectivityReceiver.startConnectivityReceiver(AutomationService.getInstance());
// startCellLocationChangedReceiver
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed)))
CellLocationChangedReceiver.startCellLocationChangedReceiver();
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed)))
{
if(!Miscellaneous.googleToBlameForLocation(true))
CellLocationChangedReceiver.startCellLocationChangedReceiver();
}
// startBatteryReceiver
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel))
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel))
BatteryReceiver.startBatteryReceiver(AutomationService.getInstance());
// startAlarmListener
AlarmListener.startAlarmListener(AutomationService.getInstance());
DateTimeListener.startAlarmListener(AutomationService.getInstance());
TimeZoneListener.startTimeZoneListener(AutomationService.getInstance());
// startNoiseListener
@ -163,15 +167,15 @@ public class ReceiverCoordinator
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.process_started_stopped))
ProcessListener.startProcessListener(AutomationService.getInstance());
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.deviceOrientation))
DeviceOrientationListener.getInstance().startListener(AutomationService.getInstance());
try
{
Class testClass = Class.forName(ActivityManageRule.activityDetectionClassPath);
//startActivityDetectionReceiver
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
{
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
catch(ClassNotFoundException e)
{
@ -196,15 +200,15 @@ public class ReceiverCoordinator
WifiBroadcastReceiver.stopWifiReceiver();
BatteryReceiver.stopBatteryReceiver();
TimeZoneListener.stopTimeZoneListener();
AlarmListener.stopAlarmListener(AutomationService.getInstance());
DateTimeListener.stopAlarmListener(AutomationService.getInstance());
NoiseListener.stopNoiseListener();
ProcessListener.stopProcessListener(AutomationService.getInstance());
DeviceOrientationListener.getInstance().stopListener(AutomationService.getInstance());
try
{
Class testClass = Class.forName(ActivityManageRule.activityDetectionClassPath);
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "stopActivityDetectionReceiver", null);
// ActivityDetectionReceiver.stopActivityDetectionReceiver();
}
catch(ClassNotFoundException e)
{
@ -213,6 +217,7 @@ public class ReceiverCoordinator
BluetoothReceiver.stopBluetoothReceiver();
HeadphoneJackListener.getInstance().stopListener(AutomationService.getInstance());
DeviceOrientationListener.getInstance().stopListener(AutomationService.getInstance());
}
catch(Exception e)
{
@ -268,34 +273,46 @@ public class ReceiverCoordinator
ProcessListener.stopProcessListener(AutomationService.getInstance());
}
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor"))
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "haveAllPermission", null);
if(haveAllPerms)
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "restartActivityDetectionReceiver", null);
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if (runResult instanceof Boolean)
{
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "restartActivityDetectionReceiver", null);
// ActivityDetectionReceiver.restartActivityDetectionReceiver();
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "haveAllPermission", null);
if(haveAllPerms)
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
else
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "stopActivityDetectionReceiver", null);
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if (runResult instanceof Boolean)
{
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective(activityDetectionClassPath, "stopActivityDetectionReceiver", null);
// ActivityDetectionReceiver.stopActivityDetectionReceiver();
}
}
}
}
@ -335,6 +352,24 @@ public class ReceiverCoordinator
}
}
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.deviceOrientation))
{
if(!DeviceOrientationListener.getInstance().isListenerRunning())
{
Miscellaneous.logEvent("i", "DevicePositionListener", "Starting DevicePositionListener because used in a new/changed rule.", 4);
// if(DevicePositionListener.getInstance().haveAllPermission())
DeviceOrientationListener.getInstance().startListener(AutomationService.getInstance());
}
}
else
{
if(DeviceOrientationListener.getInstance().isListenerRunning())
{
Miscellaneous.logEvent("i", "DevicePositionListener", "Shutting down DevicePositionListener because not used in any rule.", 4);
DeviceOrientationListener.getInstance().stopListener(AutomationService.getInstance());
}
}
AutomationService.updateNotification();
}
}

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
@ -14,7 +15,11 @@ public class Settings implements SharedPreferences
public final static int lockSoundChangesInterval = 15;
public static final int newsPollEveryXDays = 3;
public static final int newsDisplayForXDays = 3;
public static final int updateCheckFrequencyDays = 7;
public static final String folderName = "Automation";
public static final String zipFileName = "automation.zip";
public static final String constNewsOptInDone ="newsOptInDone";
public static long minimumDistanceChangeForGpsUpdate;
public static long minimumDistanceChangeForNetworkUpdate;
@ -51,16 +56,23 @@ public class Settings implements SharedPreferences
public static boolean rememberLastActivePoi;
public static int locationRingBufferSize;
public static long timeBetweenProcessMonitorings;
public static long acceptDeviceOrientationSignalEveryX_MilliSeconds;
public static int activityDetectionFrequency;
public static int activityDetectionRequiredProbability;
public static boolean privacyLocationing;
public static int startScreen;
public static int tabsPlacement;
public static boolean executeRulesAndProfilesWithSingleClick;
public static boolean displayNewsOnMainScreen;
public static boolean automaticUpdateCheck;
public static boolean lockSoundChanges;
public static boolean noticeAndroid9MicrophoneShown;
public static boolean noticeAndroid10WifiShown;
public static long lastNewsPolltime;
public static long lastUpdateCheck;
public static ArrayList<String> whatHasBeenDone;
/*
Generic settings valid for all installations and not changable
@ -102,14 +114,18 @@ public class Settings implements SharedPreferences
protected static final boolean default_rememberLastActivePoi = true;
protected static final int default_locationRingBufferSize=3;
protected static final long default_timeBetweenProcessMonitorings = 60;
protected static final long default_acceptDevicePositionSignalEveryX_MilliSeconds = 1000;
protected static final int default_activityDetectionFrequency = 60;
protected static final int default_activityDetectionRequiredProbability = 75;
protected static final boolean default_privacyLocationing = false;
protected static final int default_startScreen = 0;
protected static final int default_tabsPlacement = 0;
protected static final boolean default_executeRulesAndProfilesWithSingleClick = false;
protected static final boolean default_displayNewsOnMainScreen = false;
protected static final boolean default_automaticUpdateCheck = false;
protected static final boolean default_lockSoundChanges = false;
protected static final long default_lastNewsPolltime = -1;
protected static final long default_lastUpdateCheck = -1;
@Override
public boolean contains(String arg0)
@ -222,6 +238,7 @@ public class Settings implements SharedPreferences
lengthOfNoiseLevelMeasurements = Long.parseLong(prefs.getString("lengthOfNoiseLevelMeasurements", String.valueOf(default_lengthOfNoiseLevelMeasurements)));
referenceValueForNoiseLevelMeasurements = Long.parseLong(prefs.getString("referenceValueForNoiseLevelMeasurements", String.valueOf(default_referenceValueForNoiseLevelMeasurements)));
timeBetweenProcessMonitorings = Long.parseLong(prefs.getString("timeBetweenProcessMonitorings", String.valueOf(default_timeBetweenProcessMonitorings)));
acceptDeviceOrientationSignalEveryX_MilliSeconds = Long.parseLong(prefs.getString("acceptDevicePositionSignalEveryX_MilliSeconds", String.valueOf(default_acceptDevicePositionSignalEveryX_MilliSeconds)));
httpAcceptAllCertificates = prefs.getBoolean("httpAcceptAllCertificates", default_httpAcceptAllCertificates);
httpAttempts = Integer.parseInt(prefs.getString("httpAttempts", String.valueOf(default_httpAttempts)));
@ -241,8 +258,10 @@ public class Settings implements SharedPreferences
privacyLocationing = prefs.getBoolean("privacyLocationing", default_privacyLocationing);
startScreen = Integer.parseInt(prefs.getString("startScreen", String.valueOf(default_startScreen)));
tabsPlacement = Integer.parseInt(prefs.getString("tabsPlacement", String.valueOf(default_tabsPlacement)));
executeRulesAndProfilesWithSingleClick = prefs.getBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick);
automaticUpdateCheck = prefs.getBoolean("automaticUpdateCheck", default_automaticUpdateCheck);
displayNewsOnMainScreen = prefs.getBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen);
lockSoundChanges = prefs.getBoolean("lockSoundChanges", default_lockSoundChanges);
@ -250,6 +269,17 @@ public class Settings implements SharedPreferences
noticeAndroid10WifiShown = prefs.getBoolean("noticeAndroid10WifiShown", false);
lastNewsPolltime = prefs.getLong("lastNewsPolltime", default_lastNewsPolltime);
lastUpdateCheck = prefs.getLong("lastUpdateCheck", default_lastUpdateCheck);
String whbdString = prefs.getString("whatHasBeenDone", "");
if(whbdString != null && whbdString.length() > 0)
{
whatHasBeenDone = new ArrayList<>();
for(String s : whbdString.split(";"))
{
whatHasBeenDone.add(s);
}
}
}
catch(Exception e)
{
@ -261,6 +291,26 @@ public class Settings implements SharedPreferences
initializeSettings(context, false);
}
}
public static void considerDone(String key)
{
if(whatHasBeenDone == null)
whatHasBeenDone = new ArrayList<>();
if(!whatHasBeenDone.contains(key))
whatHasBeenDone.add(key);
}
public static boolean hasBeenDone(String key)
{
if(whatHasBeenDone != null)
{
if(whatHasBeenDone.contains(key))
return true;
}
return false;
}
/**Makes sure a settings has a valid setting. If not it will assign a reasonable default setting to it.
* If force settings will be initialized even if the user has set something.**/
@ -384,6 +434,9 @@ public class Settings implements SharedPreferences
if(!prefs.contains("timeBetweenProcessMonitorings") | force)
editor.putString("timeBetweenProcessMonitorings", String.valueOf(default_timeBetweenProcessMonitorings));
if(!prefs.contains("acceptDevicePositionSignalEveryX_MilliSeconds") | force)
editor.putString("acceptDevicePositionSignalEveryX_MilliSeconds", String.valueOf(default_acceptDevicePositionSignalEveryX_MilliSeconds));
if(!prefs.contains("activityDetectionFrequency") | force)
editor.putString("activityDetectionFrequency", String.valueOf(default_activityDetectionFrequency));
@ -396,9 +449,15 @@ public class Settings implements SharedPreferences
if(!prefs.contains("startScreen") | force)
editor.putString("startScreen", String.valueOf(default_startScreen));
if(!prefs.contains("tabsPlacement") | force)
editor.putString("tabsPlacement", String.valueOf(default_tabsPlacement));
if(!prefs.contains("executeRulesAndProfilesWithSingleClick") | force)
editor.putBoolean("executeRulesAndProfilesWithSingleClick", default_executeRulesAndProfilesWithSingleClick);
if(!prefs.contains("automaticUpdateCheck") | force)
editor.putBoolean("automaticUpdateCheck", default_automaticUpdateCheck);
if(!prefs.contains("displayNewsOnMainScreen") | force)
editor.putBoolean("displayNewsOnMainScreen", default_displayNewsOnMainScreen);
@ -410,6 +469,12 @@ public class Settings implements SharedPreferences
if(!prefs.contains("lastNewsPolltime") | force)
editor.putLong("lastNewsPolltime", default_lastNewsPolltime);
if(!prefs.contains("lastUpdateCheck") | force)
editor.putLong("lastUpdateCheck", default_lastUpdateCheck);
if(!prefs.contains("whatHasBeenDone") | force)
editor.putString("whatHasBeenDone", "");
editor.commit();
@ -467,11 +532,14 @@ public class Settings implements SharedPreferences
editor.putString("httpAttemptGap", String.valueOf(httpAttemptGap));
editor.putString("locationRingBufferSize", String.valueOf(locationRingBufferSize));
editor.putString("timeBetweenProcessMonitorings", String.valueOf(timeBetweenProcessMonitorings));
editor.putString("acceptDevicePositionSignalEveryX_MilliSeconds", String.valueOf(acceptDeviceOrientationSignalEveryX_MilliSeconds));
editor.putString("activityDetectionFrequency", String.valueOf(activityDetectionFrequency));
editor.putString("activityDetectionRequiredProbability", String.valueOf(activityDetectionRequiredProbability));
editor.putBoolean("privacyLocationing", privacyLocationing);
editor.putString("startScreen", String.valueOf(startScreen));
editor.putString("tabsPlacement", String.valueOf(tabsPlacement));
editor.putBoolean("executeRulesAndProfilesWithSingleClick", executeRulesAndProfilesWithSingleClick);
editor.putBoolean("automaticUpdateCheck", automaticUpdateCheck);
editor.putBoolean("displayNewsOnMainScreen", displayNewsOnMainScreen);
editor.putBoolean("lockSoundChanges", lockSoundChanges);
@ -479,6 +547,9 @@ public class Settings implements SharedPreferences
editor.putBoolean("noticeAndroid10WifiShown", noticeAndroid10WifiShown);
editor.putLong("lastNewsPolltime", lastNewsPolltime);
editor.putLong("lastUpdateCheck", lastUpdateCheck);
editor.putString("whatHasBeenDone", Miscellaneous.explode(";", whatHasBeenDone));
if(lastActivePoi == null)
editor.putString("lastActivePoi", "null");
@ -518,4 +589,4 @@ public class Settings implements SharedPreferences
return null;
}
}
}

View File

@ -6,70 +6,90 @@ import java.util.ArrayList;
public class TimeFrame
{
// Defines a timeframe
private Time triggerTimeStart;
private Time triggerTimeStop;
protected Time triggerTimeStart;
protected Time triggerTimeStop;
protected long repetition;
private ArrayList<Integer> dayList = new ArrayList<Integer>();
public ArrayList<Integer> getDayList()
protected final static String separator = "/";
private ArrayList<Integer> dayList = new ArrayList<Integer>();
public ArrayList<Integer> getDayList()
{
return dayList;
}
public void setDayList(ArrayList<Integer> dayList)
{
this.dayList = dayList;
}
public void setDayListFromString(String dayListString)
{
// Log.i("Parsing", "Full string: " + dayListString);
char[] dayListCharArray = dayListString.toCharArray();
dayList = new ArrayList<Integer>();
for(char item : dayListCharArray)
{
return dayList;
}
public void setDayList(ArrayList<Integer> dayList)
{
this.dayList = dayList;
}
public void setDayListFromString(String dayListString)
{
// Log.i("Parsing", "Full string: " + dayListString);
char[] dayListCharArray = dayListString.toCharArray();
dayList = new ArrayList<Integer>();
for(char item : dayListCharArray)
{
// Log.i("Parsing", String.valueOf(item));
dayList.add(Integer.parseInt(String.valueOf(item)));
}
dayList.add(Integer.parseInt(String.valueOf(item)));
}
}
public Time getTriggerTimeStart()
{
return triggerTimeStart;
}
public void setTriggerTimeStart(Time triggerTimeStart)
{
this.triggerTimeStart = triggerTimeStart;
}
public Time getTriggerTimeStop()
{
return triggerTimeStop;
}
public void setTriggerTimeStop(Time triggerTimeStop)
{
this.triggerTimeStop = triggerTimeStop;
}
public TimeFrame (Time timeStart, Time timeEnd, ArrayList<Integer> dayList2)
{
this.setTriggerTimeStart(timeStart);
this.setTriggerTimeStop(timeEnd);
this.setDayList(dayList2);
}
TimeFrame (String fileContent)
{
String[] dateArray = fileContent.split("/"); // example: timestart/timestop/days[int]
this.setTriggerTimeStart(Time.valueOf(dateArray[0]));
this.setTriggerTimeStop(Time.valueOf(dateArray[1]));
this.setDayListFromString(dateArray[2]);
}
@Override
public String toString()
{
String returnString = this.getTriggerTimeStart().toString() + "/" + this.getTriggerTimeStop().toString() + "/";
for(Integer oneDay : this.getDayList())
returnString += String.valueOf(oneDay);
return returnString;
}
}
public Time getTriggerTimeStart()
{
return triggerTimeStart;
}
public void setTriggerTimeStart(Time triggerTimeStart)
{
this.triggerTimeStart = triggerTimeStart;
}
public Time getTriggerTimeStop()
{
return triggerTimeStop;
}
public void setTriggerTimeStop(Time triggerTimeStop)
{
this.triggerTimeStop = triggerTimeStop;
}
public long getRepetition()
{
return repetition;
}
public void setRepetition(long repetition)
{
this.repetition = repetition;
}
public TimeFrame (Time timeStart, Time timeEnd, ArrayList<Integer> dayList2, long repetition)
{
this.setTriggerTimeStart(timeStart);
this.setTriggerTimeStop(timeEnd);
this.setDayList(dayList2);
this.setRepetition(repetition);
}
public TimeFrame (String fileContent)
{
String[] dateArray = fileContent.split(separator); // example: timestart/timestop/days[int]/repetition
this.setTriggerTimeStart(Time.valueOf(dateArray[0]));
this.setTriggerTimeStop(Time.valueOf(dateArray[1]));
this.setDayListFromString(dateArray[2]);
if(dateArray.length > 3) // may not exist in old config files
this.setRepetition(Long.parseLong(dateArray[3]));
}
@Override
public String toString()
{
String returnString = this.getTriggerTimeStart().toString() + separator + this.getTriggerTimeStop().toString() + separator;
for(Integer oneDay : this.getDayList())
returnString += String.valueOf(oneDay);
returnString += separator + String.valueOf(repetition);
return returnString;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,8 @@ import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import static com.jens.automation2.Trigger.triggerParameter2Split;
public class XmlFileInterface
{
public static String settingsFileName = "Automation_settings.xml";
@ -91,7 +93,7 @@ public class XmlFileInterface
{
//start a tag called "root"
serializer.startTag(null, "PointOfInterest");
//i indent code just to have a view similar to xml-tree
serializer.startTag(null, "name");
serializer.text(PointOfInterest.getPointOfInterestCollection().get(i).getName());
@ -168,9 +170,9 @@ public class XmlFileInterface
serializer.startTag(null, "changeVibrateWhenRinging");
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getChangeVibrateWhenRinging()));
serializer.endTag(null, "changeVibrateWhenRinging");//
serializer.startTag(null, "changeVibrateWhenRinging");
serializer.startTag(null, "vibrateWhenRinging");
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getVibrateWhenRinging()));
serializer.endTag(null, "changeVibrateWhenRinging");
serializer.endTag(null, "vibrateWhenRinging");
serializer.startTag(null, "changeNotificationRingtone");
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getChangeNotificationRingtone()));
@ -202,13 +204,19 @@ public class XmlFileInterface
serializer.endTag(null, "changeHapticFeedback");//
serializer.startTag(null, "hapticFeedback");
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getHapticFeedback()));
serializer.endTag(null, "hapticFeedback");
serializer.endTag(null, "hapticFeedback");
serializer.startTag(null, "changeDndMode");
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getChangeDndMode()));
serializer.endTag(null, "changeDndMode");//
serializer.startTag(null, "dndMode");
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getDndMode()));
serializer.endTag(null, "dndMode");
serializer.endTag(null, "Profile");
}
serializer.endTag(null, "ProfileCollection");
serializer.startTag(null, "RuleCollection");
for(int i=0; i<Rule.getRuleCollection().size(); i++)
@ -253,13 +261,13 @@ public class XmlFileInterface
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.noiseLevel)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getNoiseLevelDb()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.wifiConnection)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getWifiName());
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerParameter2());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.process_started_stopped)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getProcessName());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.batteryLevel)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getBatteryLevel()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.phoneCall)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneDirection()) + "," + String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneNumber()));
// else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.phoneCall)
// serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneDirection()) + "," + String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getPhoneNumber()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.nfcTag)
serializer.text(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getNfcTagId());
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.activityDetection)
@ -271,6 +279,10 @@ public class XmlFileInterface
}
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.headsetPlugged)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getHeadphoneType()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.notification)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerParameter2()));
else
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerParameter2()));
serializer.endTag(null, "TriggerParameter2");
serializer.endTag(null, "Trigger");
}
@ -377,7 +389,7 @@ public class XmlFileInterface
}
catch (XmlPullParserException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch(FileNotFoundException e)
{
@ -392,12 +404,12 @@ public class XmlFileInterface
}
catch(Exception ex)
{
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
catch (IOException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch(Exception e)
{
@ -532,11 +544,11 @@ public class XmlFileInterface
}
catch (NumberFormatException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch (Exception e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
else
@ -601,6 +613,10 @@ public class XmlFileInterface
newProfile.setChangeSoundMode(Boolean.parseBoolean(readTag(parser, "changeSoundMode")));
else if (name.equals("soundMode"))
newProfile.setSoundMode(Integer.parseInt(readTag(parser, "soundMode")));
else if (name.equals("changeDndMode"))
newProfile.setChangeDndMode(Boolean.parseBoolean(readTag(parser, "changeDndMode")));
else if (name.equals("dndMode"))
newProfile.setDndMode(Integer.parseInt(readTag(parser, "dndMode")));
else if (name.equals("changeVolumeMusicVideoGameMedia"))
newProfile.setChangeVolumeMusicVideoGameMedia(Boolean.parseBoolean(readTag(parser, "changeVolumeMusicVideoGameMedia")));
else if (name.equals("volumeMusic"))
@ -635,6 +651,8 @@ public class XmlFileInterface
else
newProfile.setNotificationRingtone(null);
}
else if (name.equals("vibrateWhenRinging"))
newProfile.setVibrateWhenRinging(Boolean.parseBoolean(readTag(parser, "vibrateWhenRinging")));
else if (name.equals("changeAudibleSelection"))
newProfile.setChangeAudibleSelection(Boolean.parseBoolean(readTag(parser, "changeAudibleSelection")));
else if (name.equals("audibleSelection"))
@ -746,16 +764,16 @@ public class XmlFileInterface
try
{
newRule.setTriggerSet(readTriggerCollection(parser));
for(Trigger t : newRule.getTriggerSet())
t.setParentRule(newRule);
}
catch (XmlPullParserException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
else if (name.equals("ActionCollection"))
@ -763,16 +781,16 @@ public class XmlFileInterface
try
{
newRule.setActionSet(readActionCollection(parser));
for(Action a : newRule.getActionSet())
a.setParentRule(newRule);
}
catch (XmlPullParserException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
else
@ -801,7 +819,15 @@ public class XmlFileInterface
// Starts by looking for the entry tag
if (name.equals("Trigger"))
{
triggerCollection.add(readTrigger(parser));
try
{
triggerCollection.add(readTrigger(parser));
}
catch (IllegalArgumentException | NullPointerException e)
{
Miscellaneous.logEvent("e", "XMLFileInterface", "Unknown trigger found in config file. File was probably created by a newer program version. Details: " + Log.getStackTraceString(e), 1);
Miscellaneous.messageBox(context.getString(R.string.error), context.getString(R.string.elementSkipped), context).show();
}
}
else
{
@ -812,7 +838,6 @@ public class XmlFileInterface
return (triggerCollection);
}
private static Trigger readTrigger(XmlPullParser parser) throws IOException, XmlPullParserException
{
@ -863,38 +888,11 @@ public class XmlFileInterface
if (name.equals("TriggerEvent"))
{
String triggerEventString = readTag(parser, "TriggerEvent");
if(triggerEventString.equals("pointOfInterest"))
newTrigger.setTriggerType(Trigger_Enum.pointOfInterest);
else if(triggerEventString.equals("timeFrame"))
newTrigger.setTriggerType(Trigger_Enum.timeFrame);
else if(triggerEventString.equals("charging"))
newTrigger.setTriggerType(Trigger_Enum.charging);
else if(triggerEventString.equals("usb_host_connection"))
newTrigger.setTriggerType(Trigger_Enum.usb_host_connection);
else if(triggerEventString.equals("batteryLevel"))
newTrigger.setTriggerType(Trigger_Enum.batteryLevel);
else if(triggerEventString.equals("speed"))
newTrigger.setTriggerType(Trigger_Enum.speed);
else if(triggerEventString.equals("noiseLevel"))
newTrigger.setTriggerType(Trigger_Enum.noiseLevel);
else if(triggerEventString.equals("wifiConnection"))
newTrigger.setTriggerType(Trigger_Enum.wifiConnection);
else if(triggerEventString.equals("process_started_stopped") | triggerEventString.equals("process_running"))
if(triggerEventString.equals("process_started_stopped") || triggerEventString.equals("process_running"))
newTrigger.setTriggerType(Trigger_Enum.process_started_stopped);
else if(triggerEventString.equals("airplaneMode"))
newTrigger.setTriggerType(Trigger_Enum.airplaneMode);
else if(triggerEventString.equals("roaming"))
newTrigger.setTriggerType(Trigger_Enum.roaming);
else if(triggerEventString.equals("phoneCall"))
newTrigger.setTriggerType(Trigger_Enum.phoneCall);
else if(triggerEventString.equals("nfcTag"))
newTrigger.setTriggerType(Trigger_Enum.nfcTag);
else if(triggerEventString.equals("activityDetection"))
newTrigger.setTriggerType(Trigger_Enum.activityDetection);
else if(triggerEventString.equals("bluetoothConnection"))
newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection);
else if(triggerEventString.equals("headsetPlugged"))
newTrigger.setTriggerType(Trigger_Enum.headsetPlugged);
else
newTrigger.setTriggerType(Trigger_Enum.valueOf(triggerEventString));
}
else if (name.equals("TriggerParameter1"))
{
@ -917,42 +915,90 @@ public class XmlFileInterface
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 2);
Toast.makeText(context, "Error while writing file: " + Log.getStackTraceString(e), Toast.LENGTH_LONG).show();
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.timeFrame)
{
newTrigger.setTimeFrame(new TimeFrame(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.batteryLevel)
{
newTrigger.setBatteryLevel(Integer.parseInt(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.speed)
{
newTrigger.setSpeed(Double.parseDouble(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.noiseLevel)
{
newTrigger.setNoiseLevelDb(Long.parseLong(triggerParameter2));
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.wifiConnection)
{
newTrigger.setWifiName(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.process_started_stopped)
{
newTrigger.setProcessName(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.phoneCall)
{
// 0/1/2,number
int direction = Integer.parseInt(triggerParameter2.substring(0, 1));
String number = triggerParameter2.substring(2);
newTrigger.setPhoneDirection(direction);
newTrigger.setPhoneNumber(number);
String[] elements = triggerParameter2.split(",");
if(elements.length == 2) //old format
{
// 0/1/2,number
int direction = Integer.parseInt(elements[0]);
String number = elements[1];
newTrigger.setPhoneDirection(direction);
newTrigger.setPhoneNumber(number);
String tp2String = "";
if(newTrigger.getTriggerParameter())
tp2String+= Trigger.triggerPhoneCallStateStarted;
else
tp2String+= Trigger.triggerPhoneCallStateStopped;
tp2String += triggerParameter2Split;
switch(direction)
{
case 0:
tp2String += Trigger.triggerPhoneCallDirectionAny;
break;
case 1:
tp2String += Trigger.triggerPhoneCallDirectionIncoming;
break;
case 2:
tp2String += Trigger.triggerPhoneCallDirectionOutgoing;
break;
}
tp2String += triggerParameter2Split;
tp2String += number;
newTrigger.setTriggerParameter2(tp2String);
}
/*else // new format
{
//tp1 is now irrelevant
elements = triggerParameter2.split(Trigger.triggerParameter2Split);
// state/direction/number
}*/
else
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.nfcTag)
{
newTrigger.setNfcTagId(triggerParameter2);
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.activityDetection)
{
@ -964,6 +1010,7 @@ public class XmlFileInterface
{
newTrigger.setActivityDetectionType(0);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.bluetoothConnection)
{
@ -973,6 +1020,7 @@ public class XmlFileInterface
newTrigger.setBluetoothEvent(substrings[0]);
newTrigger.setBluetoothDeviceAddress(substrings[1]);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else if(newTrigger.getTriggerType() == Trigger_Enum.headsetPlugged)
{
@ -984,7 +1032,10 @@ public class XmlFileInterface
{
newTrigger.setHeadphoneType(-1);
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else
newTrigger.setTriggerParameter2(triggerParameter2);
}
else
{
@ -1010,7 +1061,15 @@ public class XmlFileInterface
// Starts by looking for the entry tag
if (name.equals("Action"))
{
actionCollection.add(readAction(parser));
try
{
actionCollection.add(readAction(parser));
}
catch (IllegalArgumentException | NullPointerException e)
{
Miscellaneous.logEvent("e", "XMLFileInterface", "Unknown action found in config file. File was probably created by a newer program version. Details: " + Log.getStackTraceString(e), 1);
Miscellaneous.messageBox(context.getString(R.string.error), context.getString(R.string.elementSkipped), context).show();
}
}
else
{
@ -1019,7 +1078,6 @@ public class XmlFileInterface
}
return (actionCollection);
}
private static Action readAction(XmlPullParser parser) throws IOException, XmlPullParserException
{
@ -1072,19 +1130,9 @@ public class XmlFileInterface
{
String actionNameString = readTag(parser, "ActionName");
if(actionNameString.equals("setWifi"))
newAction.setAction(Action_Enum.setWifi);
else if(actionNameString.equals("setBluetooth"))
newAction.setAction(Action_Enum.setBluetooth);
else if(actionNameString.equals("setUsbTethering"))
newAction.setAction(Action_Enum.setUsbTethering);
else if(actionNameString.equals("setWifiTethering"))
newAction.setAction(Action_Enum.setWifiTethering);
else if(actionNameString.equals("setDisplayRotation"))
newAction.setAction(Action_Enum.setDisplayRotation);
// *** deprecated
else if(actionNameString.equals("turnWifiOn"))
//else
if(actionNameString.equals("turnWifiOn"))
newAction.setAction(Action_Enum.turnWifiOn);
else if(actionNameString.equals("turnWifiOff"))
newAction.setAction(Action_Enum.turnWifiOff);
@ -1104,30 +1152,17 @@ public class XmlFileInterface
newAction.setAction(Action_Enum.enableScreenRotation);
else if(actionNameString.equals("disableScreenRotation"))
newAction.setAction(Action_Enum.disableScreenRotation);
// *** deprecated
else if(actionNameString.equals("triggerUrl"))
newAction.setAction(Action_Enum.triggerUrl);
else if(actionNameString.equals("changeSoundProfile"))
newAction.setAction(Action_Enum.changeSoundProfile);
else if(actionNameString.equals("startOtherActivity"))
newAction.setAction(Action_Enum.startOtherActivity);
else if(actionNameString.equals("waitBeforeNextAction"))
newAction.setAction(Action_Enum.waitBeforeNextAction);
else if(actionNameString.equals("wakeupDevice"))
newAction.setAction(Action_Enum.wakeupDevice);
else if(actionNameString.equals("setAirplaneMode"))
newAction.setAction(Action_Enum.setAirplaneMode);
else if(actionNameString.equals("setDataConnection"))
newAction.setAction(Action_Enum.setDataConnection);
else if(actionNameString.equals("speakText"))
newAction.setAction(Action_Enum.speakText);
else if(actionNameString.equals("sendTextMessage"))
newAction.setAction(Action_Enum.sendTextMessage);
else if(actionNameString.equals("playMusic"))
newAction.setAction(Action_Enum.playMusic);
else if(actionNameString.equals("setScreenBrightness"))
newAction.setAction(Action_Enum.setScreenBrightness);
else if(actionNameString.equals("disableScreenRotation"))
newAction.setAction(Action_Enum.disableScreenRotation);
else if(actionNameString.equals("wakeupDevice"))
{
newAction.setAction(Action_Enum.turnScreenOnOrOff);
newAction.setParameter1(true);
}
// *** deprecated
else
newAction.setAction(Action_Enum.valueOf(actionNameString));
}
else if (name.equals("ActionParameter1"))
{
@ -1192,6 +1227,18 @@ public class XmlFileInterface
newAction.setParameter1(false);
readTag(parser, "ActionParameter1"); //read the tag for the parser to head on
}
else if(newAction.getAction().equals(Action_Enum.disableScreenRotation))
{
newAction.setAction(Action_Enum.setDisplayRotation);
newAction.setParameter1(false);
readTag(parser, "ActionParameter1"); //read the tag for the parser to head on
}
else if(newAction.getAction().equals(Action_Enum.turnScreenOnOrOff) && newAction.getParameter1())
{
/*
If param1 == true we will keep it because this action used to be of type wakeUpDevice.
*/
}
else
// exclusion for deprecated types
newAction.setParameter1(Boolean.parseBoolean(readTag(parser, "ActionParameter1")));
@ -1213,8 +1260,42 @@ public class XmlFileInterface
{
newAction.setParameter2(tag);
}
}
/*
androidx.security.crypto.MasterKey.Builder
MasterKey mainKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
*/
}
}
else if(newAction.getAction().equals(Action_Enum.startOtherActivity)) // separator has been changed, convert in old files
{
String newTag;
if(tag.contains(Action.intentPairSeperator)) // already has new format
newTag = tag;
else
newTag = tag.replace("/", Action.intentPairSeperator);
String[] newTagPieces = newTag.split(";");
if(newTagPieces.length < 2 || (!newTagPieces[0].contains(Actions.dummyPackageString) && newTagPieces[1].contains(Action.intentPairSeperator)))
{
newTag = Actions.dummyPackageString + ";" + newTag;
newTagPieces = newTag.split(";");
}
if(newTagPieces.length < 3)
newTag += ";" + ActivityManageActionStartActivity.startByActivityString;
else if(newTagPieces.length >= 3)
{
if(newTagPieces[2].contains(Action.intentPairSeperator))
newTag = newTagPieces[0] + ";" + newTagPieces[1] + ";" + ActivityManageActionStartActivity.startByActivityString + ";" + newTagPieces[2];
}
newAction.setParameter2(newTag);
}
else
newAction.setParameter2(tag);
}
@ -1237,9 +1318,6 @@ public class XmlFileInterface
}
}
}
// Miscellaneous.logEvent("i", "New Rule from file", newPoi.name + "/" + String.valueOf(newPoi.radius) + "/" + String.valueOf(newPoi.location.getLatitude()) + "/" + String.valueOf(newPoi.location.getLongitude()) + "/" + String.valueOf(newPoi.changeWifiState) + "/" + String.valueOf(newPoi.desiredWifiState) + "/" + String.valueOf(newPoi.changeCameraState) + "/" + String.valueOf(newPoi.desiredCameraState) + "/" + String.valueOf(newPoi.changeSoundSetting) + "/" + String.valueOf(newPoi.desiredSoundSetting));
return newAction;
}

View File

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

View File

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

View File

@ -114,7 +114,14 @@ public class CellLocationChangedReceiver extends PhoneStateListener
myLocationManager = (LocationManager) AutomationService.getInstance().getSystemService(Context.LOCATION_SERVICE);
currentLocation = getLocation("coarse");
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false);
try
{
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false);
}
catch(NullPointerException e)
{
Miscellaneous.logEvent("e", "LocationProvider", "Location provider is null: " + Log.getStackTraceString(e), 1);
}
}
else
{
@ -123,6 +130,20 @@ public class CellLocationChangedReceiver extends PhoneStateListener
}
}
public static boolean isCellLocationChangedReceiverPossible()
{
if(telephonyManager == null)
telephonyManager = (TelephonyManager) AutomationService.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
if(
ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance())
||
telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY
)
return false;
else
return true;
}
public Location getLocation(String accuracy)
{
@ -131,7 +152,7 @@ public class CellLocationChangedReceiver extends PhoneStateListener
String myProviderName;
// If privacy mode or no data connection available
if(Settings.privacyLocationing | !ConnectivityReceiver.isDataConnectionAvailable(AutomationService.getInstance()))
if(Settings.privacyLocationing || !ConnectivityReceiver.isDataConnectionAvailable(AutomationService.getInstance()))
{
Miscellaneous.logEvent("i", "CellLocation", Miscellaneous.getAnyContext().getResources().getString(R.string.enforcingGps), 4);
myProviderName = LocationManager.GPS_PROVIDER;
@ -169,6 +190,9 @@ public class CellLocationChangedReceiver extends PhoneStateListener
}
else
{
if(myLocationManager == null)
myLocationManager = (LocationManager) AutomationService.getInstance().getSystemService(Context.LOCATION_SERVICE);
if(!myLocationManager.isProviderEnabled(myProviderName))
{
if(myProviderName.equals(LocationManager.NETWORK_PROVIDER))
@ -220,13 +244,11 @@ public class CellLocationChangedReceiver extends PhoneStateListener
return currentLocation;
}
public void setCurrentLocation(Location currentLocation)
{
this.currentLocation = currentLocation;
}
public class MyLocationListener implements LocationListener
{
@Override
@ -316,12 +338,12 @@ public class CellLocationChangedReceiver extends PhoneStateListener
{
if(telephonyManager == null)
telephonyManager = (TelephonyManager) AutomationService.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
try
{
if(!cellLocationListenerActive)
{
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()))
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY)
{
if(WifiBroadcastReceiver.mayCellLocationReceiverBeActivated())
{
@ -347,10 +369,10 @@ public class CellLocationChangedReceiver extends PhoneStateListener
// Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because we have no data connection.", 4);
}
else
Miscellaneous.logEvent("w", "cellReceiver", "Wanted to activate CellLocationChangedReceiver, but Wifi-Receiver says not to.", 4);
Miscellaneous.logEvent("w", "cellReceiver", "Wanted to activate CellLocationChangedReceiver, but Wifi-Receiver says not to.", 4);
}
else
Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because Airplane mode is active.", 4);
Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because Airplane mode is active or SIM_STATE is not ready.", 4);
}
}
catch(Exception ex)
@ -403,5 +425,4 @@ public class CellLocationChangedReceiver extends PhoneStateListener
&&
ActivityPermissions.havePermission("android.permission.ACCESS_WIFI_STATE", Miscellaneous.getAnyContext());
}
}
}

View File

@ -7,6 +7,7 @@ import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.jens.automation2.ActivityMainScreen;
@ -26,13 +27,9 @@ import java.util.Calendar;
public class LocationProvider
{
protected static boolean passiveLocationListenerActive = false;
protected static LocationListener passiveLocationListener;
protected static LocationProvider locationProviderInstance = null;
protected AutomationService parentService;
public AutomationService getParentService()
{
@ -109,106 +106,128 @@ public class LocationProvider
public void setCurrentLocation(Location newLocation, boolean skipVerification)
{
Miscellaneous.logEvent("i", "Location", "Setting location.", 4);
currentLocation = newLocation;
currentLocationStaticCopy = newLocation;
Miscellaneous.logEvent("i", "LocationListener", "Giving update to POI class", 4);
PointOfInterest.positionUpdate(newLocation, parentService, false, skipVerification);
try
if(newLocation != null)
{
if(
locationList.size() >= 1
&&
locationList.get(locationList.size()-1).getTime() == newLocation.getTime()
&&
locationList.get(locationList.size()-1).getProvider().equals(newLocation.getProvider())
Miscellaneous.logEvent("i", "Location", "Setting location.", 4);
currentLocation = newLocation;
currentLocationStaticCopy = newLocation;
Miscellaneous.logEvent("i", "LocationListener", "Giving update to POI class", 4);
PointOfInterest.positionUpdate(newLocation, parentService, false, skipVerification);
try
{
if (
locationList.size() >= 1
&&
locationList.get(locationList.size() - 1).getTime() == newLocation.getTime()
&&
locationList.get(locationList.size() - 1).getProvider().equals(newLocation.getProvider())
)
{
// This is a duplicate update, do not store it
Miscellaneous.logEvent("i", "LocationListener", "Duplicate location, ignoring.", 4);
}
else
{
Miscellaneous.logEvent("i", "Speed", "Commencing speed calculation.", 4);
// This part keeps the last two location entries to determine the current speed.
locationList.add(newLocation);
if(newLocation.hasSpeed())
{
Miscellaneous.logEvent("i", "Speed", "Location has speed, taking that: " + String.valueOf(newLocation.getSpeed()) + " km/h", 4);
setSpeed(newLocation.getSpeed()); // Take the value that came with the location, that should be more precise
// This is a duplicate update, do not store it
Miscellaneous.logEvent("i", "LocationListener", "Duplicate location, ignoring.", 4);
}
else
{
if (locationList.size() >= 2)
Miscellaneous.logEvent("i", "Speed", "Commencing speed calculation.", 4);
// This part keeps the last two location entries to determine the current speed.
locationList.add(newLocation);
if (newLocation.hasSpeed())
{
Miscellaneous.logEvent("i", "Speed", "Trying to calculate speed based on the last locations.", 4);
double currentSpeed;
long timeDifferenceInSeconds = (Math.abs(locationList.get(locationList.size() - 2).getTime() - locationList.get(locationList.size() - 1).getTime())) / 1000; //milliseconds
if (timeDifferenceInSeconds <= Settings.speedMaximumTimeBetweenLocations * 60)
Miscellaneous.logEvent("i", "Speed", "Location has speed, taking that: " + String.valueOf(newLocation.getSpeed()) + " km/h", 4);
setSpeed(newLocation.getSpeed()); // Take the value that came with the location, that should be more precise
}
else
{
speedCalculation:
if (locationList.size() >= 2)
{
double distanceTraveled = locationList.get(locationList.size() - 2).distanceTo(locationList.get(locationList.size() - 1)); //results in meters
if (timeDifferenceInSeconds == 0)
while (locationList.size() > 2)
{
Miscellaneous.logEvent("w", "Speed", "No time passed since last position. Can't calculate speed here.", 4);
return;
// 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);
}
currentSpeed = distanceTraveled / timeDifferenceInSeconds * 3.6; // convert m/s --> km/h
/*
The two most recent locations in the list must have a usable accuracy.
*/
for (int i = 0; i < 2; i++)
{
if
(
(locationList.get(i).getProvider().equals(LocationManager.GPS_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyGps)
||
(locationList.get(i).getProvider().equals(LocationManager.NETWORK_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyNetwork)
)
{
Miscellaneous.logEvent("i", "Speed", "Not using 2 most recent locations for speed calculation because at least one does not have a satisfactory accuracy: " + locationList.get(i).toString(), 4);
break speedCalculation;
}
}
Miscellaneous.logEvent("i", "Speed", "Trying to calculate speed based on the last locations.", 4);
double currentSpeed;
long timeDifferenceInSeconds = (Math.abs(locationList.get(locationList.size() - 2).getTime() - locationList.get(locationList.size() - 1).getTime())) / 1000; //milliseconds
if (timeDifferenceInSeconds <= Settings.speedMaximumTimeBetweenLocations * 60)
{
double distanceTraveled = locationList.get(locationList.size() - 2).distanceTo(locationList.get(locationList.size() - 1)); //results in meters
if (timeDifferenceInSeconds == 0)
{
Miscellaneous.logEvent("w", "Speed", "No time passed since last position. Can't calculate speed here.", 4);
return;
}
currentSpeed = distanceTraveled / timeDifferenceInSeconds * 3.6; // convert m/s --> km/h
/*
Due to strange factors the time difference might be 0 resulting in mathematical error.
*/
if (Double.isInfinite(currentSpeed) | Double.isNaN(currentSpeed))
Miscellaneous.logEvent("i", "Speed", "Error while calculating speed.", 4);
else
{
Miscellaneous.logEvent("i", "Speed", "Current speed: " + String.valueOf(currentSpeed) + " km/h", 2);
setSpeed(currentSpeed);
// execute matching rules containing speed
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesBySpeed();
for (Rule oneRule : ruleCandidates)
if (Double.isInfinite(currentSpeed) | Double.isNaN(currentSpeed))
Miscellaneous.logEvent("i", "Speed", "Error while calculating speed.", 4);
else
{
if (oneRule.applies(this.getParentService()))
oneRule.activate(getParentService(), false);
Miscellaneous.logEvent("i", "Speed", "Current speed: " + String.valueOf(currentSpeed) + " km/h", 2);
setSpeed(currentSpeed);
// execute matching rules containing speed
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.speed);
for (Rule oneRule : ruleCandidates)
{
if(oneRule.getsGreenLight(this.getParentService()))
oneRule.activate(getParentService(), false);
}
}
}
else
Miscellaneous.logEvent("i", "Speed", "Last two locations are too far apart in terms of time. Cannot use them for speed calculation.", 4);
}
else
Miscellaneous.logEvent("i", "Speed", "Last two locations are too far apart in terms of time. Cannot use them for speed calculation.", 4);
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);
Miscellaneous.logEvent("w", "Speed", "Don't have enough values for speed calculation, yet.", 3);
}
}
else
{
Miscellaneous.logEvent("w", "Speed", "Don't have enough values for speed calculation, yet.", 3);
}
}
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "Speed", "Error during speed calculation: " + Log.getStackTraceString(e), 3);
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "Speed", "Error during speed calculation: " + Log.getStackTraceString(e), 3);
}
AutomationService.updateNotification();
AutomationService.updateNotification();
if(AutomationService.isMainActivityRunning(parentService))
ActivityMainScreen.updateMainScreen();
if (AutomationService.isMainActivityRunning(parentService))
ActivityMainScreen.updateMainScreen();
}
else
Miscellaneous.logEvent("w", "Location", "New location given is null. Ignoring.", 5);
}
public void startLocationService()
@ -227,13 +246,37 @@ public class LocationProvider
if(Settings.positioningEngine == 0)
{
// startCellLocationChangedReceiver
if (!ConnectivityReceiver.isAirplaneMode(this.parentService) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger_Enum.speed)))
CellLocationChangedReceiver.startCellLocationChangedReceiver();
// startPassiveLocationListener
if(Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger_Enum.speed))
{
// TelephonyManager telephonyManager = (TelephonyManager) AutomationService.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
// startCellLocationChangedReceiver
if (CellLocationChangedReceiver.isCellLocationChangedReceiverPossible())
{
if (WifiBroadcastReceiver.mayCellLocationReceiverBeActivated())
CellLocationChangedReceiver.startCellLocationChangedReceiver();
}
else
{
/*
Reasons why we may end up here:
- Airplane mode is active
- No phone module present (pure wifi device)
- No SIM card is inserted or it's not unlocked
We'd have to try GPS now to get an initial position.
For permanent use there is no way we could know when it
would make sense to check the position again.
*/
// Trigger a one-time-position-search
Location loc = CellLocationChangedReceiver.getInstance().getLocation("fine");
LocationProvider.getInstance().setCurrentLocation(loc, true);
}
// startPassiveLocationListener
startPassiveLocationListener();
}
}
else
{
@ -489,18 +532,10 @@ public class LocationProvider
{
// time is up, no cell location updates since x minutes, start accelerometer
String text = "Timer triggered. Based on the last location and speed we may be at a POI. Forcing location update in case CellLocationChangedReceiver didn\'t fire.";
// Miscellaneous.logEvent("i", "AccelerometerHandler", text, 5);
// CellLocationChangedReceiver.stopCellLocationChangedReceiver();
// startAccelerometerReceiver();
Location currentLocation = CellLocationChangedReceiver.getInstance().getLocation("coarse");
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false);
}
/*else if(msg.what == 0)
{
String text = "Abort command received, deactivating SpeedReceiver";
Miscellaneous.logEvent("i", "SpeedHandler", text, 4);
stopAccelerometerReceiver();
}*/
}
}
}

View File

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

View File

@ -1,301 +0,0 @@
package com.jens.automation2.receivers;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.Rule;
import com.jens.automation2.Trigger;
import com.jens.automation2.Trigger.Trigger_Enum;
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
public class AlarmListener extends BroadcastReceiver implements AutomationListenerInterface
{
private static AutomationService automationServiceRef;
private static AlarmManager centralAlarmManagerInstance;
// private static Intent alarmIntent;
// private static PendingIntent alarmPendingIntent;
private static boolean alarmListenerActive=false;
private static ArrayList<Long> alarmCandidates = new ArrayList<Long>();
private static ArrayList<Integer> requestCodeList = new ArrayList<Integer>();
public static void startAlarmListener(final AutomationService automationServiceRef)
{
AlarmListener.startAlarmListenerInternal(automationServiceRef);
}
public static void stopAlarmListener(Context context)
{
AlarmListener.stopAlarmListenerInternal();
}
public static boolean isAlarmListenerActive()
{
return alarmListenerActive;
}
@Override
public void onReceive(Context context, Intent intent)
{
Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
Date now = new Date();
String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds());
Time passTime = Time.valueOf(timeString);
ArrayList<Rule> allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime);
for(int i=0; i<allRulesWithNowInTimeFrame.size(); i++)
{
if(allRulesWithNowInTimeFrame.get(i).applies(context))
allRulesWithNowInTimeFrame.get(i).activate(automationServiceRef, false);
}
setAlarms();
}
public static void setAlarms()
{
alarmCandidates.clear();
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
clearAlarms();
int i=0;
// // get a Calendar object with current time
// Calendar cal = Calendar.getInstance();
// // add 5 minutes to the calendar object
// cal.add(Calendar.SECOND, 10);
// String calSetWorkingCopyString2 = null;
// SimpleDateFormat sdf2 = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
// if (cal != null)
// {
// calSetWorkingCopyString2 = sdf2.format(cal.getTime());
// }
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
// requestCodeList.add(0);
ArrayList<Rule> allRulesWithTimeFrames = new ArrayList<Rule>();
allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
for(Rule oneRule : allRulesWithTimeFrames)
{
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == Trigger_Enum.timeFrame)
{
Calendar calNow, calSet;
Time setTime;
if(oneTrigger.getTriggerParameter())
setTime = oneTrigger.getTimeFrame().getTriggerTimeStart();
else
setTime = oneTrigger.getTimeFrame().getTriggerTimeStop();
calNow = Calendar.getInstance();
calSet = (Calendar) calNow.clone();
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
calSet.set(Calendar.MINUTE, setTime.getMinutes());
calSet.set(Calendar.SECOND, 0);
calSet.set(Calendar.MILLISECOND, 0);
// At this point calSet would be a scheduling candidate. It's just the day the might not be right, yet.
long milliSecondsInAWeek = 1000 * 60 * 60 * 24 * 7;
for(int dayOfWeek : oneTrigger.getTimeFrame().getDayList())
{
Calendar calSetWorkingCopy = (Calendar) calSet.clone();
// calSetWorkingCopy.set(Calendar.HOUR_OF_DAY, setTime.getHours());
// calSetWorkingCopy.set(Calendar.MINUTE, setTime.getMinutes());
// calSetWorkingCopy.set(Calendar.SECOND, 0);
// calSetWorkingCopy.set(Calendar.MILLISECOND, 0);
int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK);
// Log.i("AlarmManager", "Today: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + " / Sched.Day: " + String.valueOf(dayOfWeek) + " Difference to target day is: " + String.valueOf(diff));
if(diff == 0) //if we're talking about the current day, is the time still in the future?
{
if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours())
{
// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours(" + String.valueOf(calSetWorkingCopy.getTime().getHours()) + ") < calNow.getTime().getHours(" + String.valueOf(calNow.getTime().getHours()) + ")");
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
}
else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours())
{
// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours()");
if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes())
{
// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getMinutes() < calNow.getTime().getMinutes()");
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
}
}
}
else if(diff < 0)
{
// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff+7) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK)));
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week
}
else
{
// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK)));
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day
}
i++;
i=(int)System.currentTimeMillis();
String calSetWorkingCopyString = sdf.format(calSetWorkingCopy.getTime()) + " RequestCode: " + String.valueOf(i);
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of rule: " + oneRule.getName() + " beginning at " + calSetWorkingCopyString);
alarmCandidates.add(calSetWorkingCopy.getTimeInMillis());
// Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
// alarmIntent.setData(Uri.parse("myalarms://" + i));
// PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, i, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, calSetWorkingCopy.getTimeInMillis(), milliSecondsInAWeek, alarmPendingIntent);
// requestCodeList.add(i);
}
}
}
}
// // get a Calendar object with current time
// Calendar cal = Calendar.getInstance();
// cal.add(Calendar.SECOND, 10);
// String calSetWorkingCopyString2 = sdf.format(cal.getTime());
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
// requestCodeList.add(0);
scheduleNextAlarm();
}
private static void scheduleNextAlarm()
{
Long currentTime = System.currentTimeMillis();
Long scheduleCandidate = null;
if(alarmCandidates.size() == 0)
{
Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
return;
}
else if(alarmCandidates.size() == 1)
{
// only one alarm, schedule that
scheduleCandidate = alarmCandidates.get(0);
}
else if(alarmCandidates.size() > 1)
{
scheduleCandidate = alarmCandidates.get(0);
for(long alarmCandidate : alarmCandidates)
{
if(Math.abs(currentTime - alarmCandidate) < Math.abs(currentTime - scheduleCandidate))
scheduleCandidate = alarmCandidate;
}
}
Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate, alarmPendingIntent);
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(scheduleCandidate);
Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4);
}
public static void clearAlarms()
{
Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
for(int requestCode : requestCodeList)
{
Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0);
// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode));
centralAlarmManagerInstance.cancel(alarmPendingIntent);
}
requestCodeList.clear();
}
private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
{
if(!alarmListenerActive)
{
Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
AlarmListener.automationServiceRef = givenAutomationServiceRef;
centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
// alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0);
alarmListenerActive = true;
Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
AlarmListener.setAlarms();
// // get a Calendar object with current time
// Calendar cal = Calendar.getInstance();
// // add 5 minutes to the calendar object
// cal.add(Calendar.SECOND, 10);
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent);
}
else
Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
}
private static void stopAlarmListenerInternal()
{
if(alarmListenerActive)
{
Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
clearAlarms();
// centralAlarmManagerInstance.cancel(alarmPendingIntent);
alarmListenerActive = false;
}
else
Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
}
public static void reloadAlarms()
{
AlarmListener.setAlarms();
}
@Override
public void startListener(AutomationService automationService)
{
AlarmListener.startAlarmListener(automationService);
}
@Override
public void stopListener(AutomationService automationService)
{
AlarmListener.stopAlarmListener(automationService);
}
public static boolean haveAllPermission()
{
return true;
}
@Override
public boolean isListenerRunning()
{
return isAlarmListenerActive();
}
@Override
public Trigger_Enum[] getMonitoredTrigger()
{
return new Trigger_Enum[] { Trigger_Enum.timeFrame };
}
}

View File

@ -1,5 +1,6 @@
package com.jens.automation2.receivers;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -18,7 +19,7 @@ import java.util.ArrayList;
public class BatteryReceiver extends BroadcastReceiver implements AutomationListenerInterface
{
private static int batteryLevel=-1; // initialize with a better value than this
private static int batteryLevel = -1; // initialize with a better value than this
public static AutomationService automationServiceRef = null;
private static boolean usbHostConnected = false;
@ -26,6 +27,7 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
private static IntentFilter batteryIntentFilter = null;
private static Intent batteryStatus = null;
private static BroadcastReceiver batteryInfoReceiverInstance = null;
public static void startBatteryReceiver(final AutomationService automationServiceRef)
{
if(!batteryReceiverActive)
@ -78,11 +80,11 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
return batteryLevel;
}
private static int deviceIsCharging = 0; //0=unknown, 1=no, 2=yes
private static int currentChargingState = 0; //0=unknown, 1=no, 2=yes
public static int getDeviceIsCharging()
public static int getCurrentChargingState()
{
return deviceIsCharging;
return currentChargingState;
}
@Override
@ -145,31 +147,6 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
this.actionDischarging(context);
break;
}
// }
// else if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED))
// {
//// Miscellaneous.logEvent("i", "BatteryReceiver", "Battery is charging or full.");
// deviceIsCharging = 2;
// //activate rule(s)
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByCharging(true);
// for(int i=0; i<ruleCandidates.size(); i++)
// {
// if(ruleCandidates.get(i).applies(context))
// ruleCandidates.get(i).activate(locationProviderRef.getParentService());
// }
// }
// else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED))
// {
//// Miscellaneous.logEvent("i", "BatteryReceiver", "Battery is discharging.");
// deviceIsCharging = 1;
// //activate rule(s)
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByCharging(false);
// for(int i=0; i<ruleCandidates.size(); i++)
// {
// if(ruleCandidates.get(i).applies(context))
// ruleCandidates.get(i).activate(locationProviderRef.getParentService());
// }
// }
}
catch(Exception e)
{
@ -180,7 +157,7 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
public static int isDeviceCharging(Context context)
{
switch(deviceIsCharging)
switch(currentChargingState)
{
case 0:
Miscellaneous.logEvent("w", "ChargingInfo", "Status of device charging was requested. Information isn't available, yet.", 4);
@ -188,25 +165,26 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
case 1:
Miscellaneous.logEvent("i", "ChargingInfo", "Status of device charging was requested. Device is discharging.", 3);
break;
case 2:
case BatteryManager.BATTERY_STATUS_CHARGING:
Miscellaneous.logEvent("i", "ChargingInfo", "Status of device charging was requested. Device is charging.", 3);
break;
}
return deviceIsCharging;
return currentChargingState;
}
private void actionCharging(Context context)
{
if(deviceIsCharging != 2) // Avoid flooding the log. This event will occur on a regular basis even though charging state wasn't changed.
if(currentChargingState != BatteryManager.BATTERY_STATUS_CHARGING) // Avoid flooding the log. This event will occur on a regular basis even though charging state wasn't changed.
{
Miscellaneous.logEvent("i", "BatteryReceiver", "Battery is charging or full.", 3);
deviceIsCharging = 2;
currentChargingState = BatteryManager.BATTERY_STATUS_CHARGING;
//activate rule(s)
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByCharging(true);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.charging);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByCharging(true);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(context))
if(ruleCandidates.get(i).getsGreenLight(context))
ruleCandidates.get(i).activate(automationServiceRef, false);
}
}
@ -216,25 +194,26 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
{
Miscellaneous.logEvent("i", "BatteryReceiver", "Battery level has changed.", 3);
//activate rule(s)
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByBatteryLevel();
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.batteryLevel);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(context))
if(ruleCandidates.get(i).getsGreenLight(context))
ruleCandidates.get(i).activate(automationServiceRef, false);
}
}
private void actionDischarging(Context context)
{
if(deviceIsCharging != 1) // Avoid flooding the log. This event will occur on a regular basis even though charging state wasn't changed.
if(currentChargingState != BatteryManager.BATTERY_STATUS_UNKNOWN) // Avoid flooding the log. This event will occur on a regular basis even though charging state wasn't changed.
{
Miscellaneous.logEvent("i", "BatteryReceiver", "Battery is discharging.", 3);
deviceIsCharging = 1;
currentChargingState = BatteryManager.BATTERY_STATUS_UNKNOWN;
//activate rule(s)
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByCharging(false);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.charging);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByCharging(false);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(context))
if(ruleCandidates.get(i).getsGreenLight(context))
ruleCandidates.get(i).activate(automationServiceRef, false);
}
@ -253,11 +232,12 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
usbHostConnected = true;
Miscellaneous.logEvent("i", "BatteryReceiver", "Connected to computer.", 3);
Toast.makeText(context, "Connected to computer.", Toast.LENGTH_LONG).show();
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByUsbHost(true);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.usb_host_connection);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByUsbHost(true);
for(Rule oneRule : ruleCandidates)
{
if(oneRule.applies(context))
if(oneRule.getsGreenLight(context))
oneRule.activate(automationServiceRef, false);
}
@ -274,11 +254,12 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
usbHostConnected = false;
Miscellaneous.logEvent("i", "BatteryReceiver", "Disconnected from computer.", 3);
Toast.makeText(context, "Disconnected from computer.", Toast.LENGTH_LONG).show();
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByUsbHost(false);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.usb_host_connection);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByUsbHost(false);
for(Rule oneRule : ruleCandidates)
{
if(oneRule.applies(context))
if(oneRule.getsGreenLight(context))
oneRule.activate(automationServiceRef, false);
}
}
@ -296,8 +277,8 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList
public static boolean haveAllPermission()
{
return ActivityPermissions.havePermission("android.permission.READ_PHONE_STATE", Miscellaneous.getAnyContext()) &&
ActivityPermissions.havePermission("android.permission.BATTERY_STATS", Miscellaneous.getAnyContext());
return ActivityPermissions.havePermission(Manifest.permission.READ_PHONE_STATE, Miscellaneous.getAnyContext()) &&
ActivityPermissions.havePermission(Manifest.permission.BATTERY_STATS, Miscellaneous.getAnyContext());
}
@Override

View File

@ -124,10 +124,10 @@ public class BluetoothReceiver extends BroadcastReceiver implements AutomationLi
Miscellaneous.logEvent("i", "BluetoothReceiver", String.format(context.getResources().getString(R.string.bluetoothDeviceInRange), bluetoothDevice.getName()), 3);
}
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByBluetoothConnection();
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.bluetoothConnection);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(AutomationService.getInstance()))
if(ruleCandidates.get(i).getsGreenLight(AutomationService.getInstance()))
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
}

View File

@ -112,7 +112,7 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
@SuppressLint("NewApi")
public static boolean isAirplaneMode(Context context)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
{
int value = android.provider.Settings.System.getInt(context.getContentResolver(), android.provider.Settings.System.AIRPLANE_MODE_ON, 0);
return value != 0;
@ -138,10 +138,11 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
boolean isAirplaneMode = isAirplaneMode(context);
automationServiceRef.getLocationProvider().handleAirplaneMode(isAirplaneMode);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByAirplaneMode(isAirplaneMode);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.airplaneMode);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByAirplaneMode(isAirplaneMode);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(automationServiceRef))
if(ruleCandidates.get(i).getsGreenLight(automationServiceRef))
ruleCandidates.get(i).activate(automationServiceRef, false);
}
}
@ -161,7 +162,7 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
WifiBroadcastReceiver.setLastWifiSsid(wifiInfo.getSSID());
WifiBroadcastReceiver.findRules(automationServiceRef.getLocationProvider());
WifiBroadcastReceiver.findRules(automationServiceRef);
break;
case ConnectivityManager.TYPE_MOBILE:
boolean isRoaming = isRoaming(context);
@ -170,11 +171,12 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
roamingLastState = isRoaming;
automationServiceRef.getLocationProvider().handleRoaming(isRoaming);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByRoaming(isRoaming);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.roaming);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByRoaming(isRoaming);
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(automationServiceRef))
if(ruleCandidates.get(i).getsGreenLight(automationServiceRef))
ruleCandidates.get(i).activate(automationServiceRef, false);
}
}
@ -219,7 +221,7 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
// This will serve as a disconnected event. Happens if wifi is connected, then module deactivated.
Miscellaneous.logEvent("i", "Connectivity", "Wifi deactivated while having been connected before.", 4);
WifiBroadcastReceiver.lastConnectedState = false;
WifiBroadcastReceiver.findRules(automationServiceRef.getLocationProvider());
WifiBroadcastReceiver.findRules(automationServiceRef);
}
}
}

View File

@ -0,0 +1,433 @@
package com.jens.automation2.receivers;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.Rule;
import com.jens.automation2.TimeFrame;
import com.jens.automation2.Trigger;
import com.jens.automation2.Trigger.Trigger_Enum;
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
public class DateTimeListener extends BroadcastReceiver implements AutomationListenerInterface
{
private static AutomationService automationServiceRef;
private static AlarmManager centralAlarmManagerInstance;
// private static Intent alarmIntent;
// private static PendingIntent alarmPendingIntent;
private static boolean alarmListenerActive=false;
private static ArrayList<ScheduleElement> alarmCandidates = new ArrayList<>();
private static ArrayList<Integer> requestCodeList = new ArrayList<Integer>();
public static void startAlarmListener(final AutomationService automationServiceRef)
{
DateTimeListener.startAlarmListenerInternal(automationServiceRef);
}
public static void stopAlarmListener(Context context)
{
DateTimeListener.stopAlarmListenerInternal();
}
public static boolean isAlarmListenerActive()
{
return alarmListenerActive;
}
@Override
public void onReceive(Context context, Intent intent)
{
Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
Date now = new Date();
String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds());
Time passTime = Time.valueOf(timeString);
ArrayList<Rule> allRulesWithNowInTimeFrame = Rule.findRuleCandidates(Trigger_Enum.timeFrame);
// ArrayList<Rule> allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime);
for(int i=0; i<allRulesWithNowInTimeFrame.size(); i++)
{
if(allRulesWithNowInTimeFrame.get(i).getsGreenLight(context))
allRulesWithNowInTimeFrame.get(i).activate(automationServiceRef, false);
}
setAlarms();
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static void setAlarms()
{
alarmCandidates.clear();
Calendar calNow = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
clearAlarms();
int i=0;
// // get a Calendar object with current time
// Calendar cal = Calendar.getInstance();
// // add 5 minutes to the calendar object
// cal.add(Calendar.SECOND, 10);
// String calSetWorkingCopyString2 = null;
// SimpleDateFormat sdf2 = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
// if (cal != null)
// {
// calSetWorkingCopyString2 = sdf2.format(cal.getTime());
// }
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
// requestCodeList.add(0);
ArrayList<Rule> allRulesWithTimeFrames = new ArrayList<Rule>();
allRulesWithTimeFrames = Rule.findRuleCandidates(Trigger_Enum.timeFrame);
// allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
/*
* Take care of regular executions, no repetitions in between.
*/
Miscellaneous.logEvent("i", "DateTimeListener", "Checking rules for single run alarm candidates.", 5);
for(Rule oneRule : allRulesWithTimeFrames)
{
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for single run alarm candidates.", 5);
if(oneRule.isRuleActive())
{
try
{
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for single run alarm candidates.", 5);
if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame))
{
TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
Calendar calSet;
Time setTime;
if(oneTrigger.getTriggerParameter())
setTime = tf.getTriggerTimeStart();
else
setTime = tf.getTriggerTimeStop();
calSet = (Calendar) calNow.clone();
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
calSet.set(Calendar.MINUTE, setTime.getMinutes());
calSet.set(Calendar.SECOND, 0);
calSet.set(Calendar.MILLISECOND, 0);
// At this point calSet would be a scheduling candidate. It's just the day that might not be right, yet.
for(int dayOfWeek : tf.getDayList())
{
Calendar calSetWorkingCopy = (Calendar) calSet.clone();
int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK);
if(diff == 0) // We're talking about the current weekday, but is the time still in the future?
{
if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours())
{
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
}
else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours())
{
if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes())
{
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
}
}
}
else if(diff < 0)
{
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week
}
else
{
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day
}
i++;
i=(int)System.currentTimeMillis();
sdf.format(calSetWorkingCopy.getTime());
String.valueOf(i);
alarmCandidates.add(new ScheduleElement(calSetWorkingCopy, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
}
}
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1);
}
}
}
/*
* Only take care of repeated executions.
*/
Miscellaneous.logEvent("i", "DateTimeListener","Checking rules for repeated run alarm candidates.", 5);
for(Rule oneRule : allRulesWithTimeFrames)
{
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for repeated run alarm candidates.", 5);
if(oneRule.isRuleActive())
{
try
{
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.toString() , 5);
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for repeated run alarm candidates.", 5);
if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame))
{
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule trigger " + oneTrigger.toString() , 5);
/*
* Check for next repeated execution:
*
* Check if the rule currently applies....
*
* If no -> do nothing
* If yes -> Take starting time and calculate the next repeated execution
* 1. Take starting time
* 2. Take current time
* 3. Calculate difference, but include check to see if we're after that time,
* be it start or end of the timeframe.
* 4. Take div result +1 and add this on top of starting time
* 5. Is this next possible execution still inside timeframe? Also consider timeframes spanning over midnight
*/
Calendar calSet;
Time setTime;
TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
if(tf.getRepetition() > 0)
{
if(oneTrigger.applies(calNow, Miscellaneous.getAnyContext()))
{
Calendar calSchedule = getNextRepeatedExecutionAfter(oneTrigger, calNow);
alarmCandidates.add(new ScheduleElement(calSchedule, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
}
}
}
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1);
}
}
}
scheduleNextAlarm();
}
private static void scheduleNextAlarm()
{
Long currentTime = System.currentTimeMillis();
ScheduleElement scheduleCandidate = null;
if(alarmCandidates.size() == 0)
{
Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
return;
}
else if(alarmCandidates.size() == 1)
{
// only one alarm, schedule that
scheduleCandidate = alarmCandidates.get(0);
}
else if(alarmCandidates.size() > 1)
{
scheduleCandidate = alarmCandidates.get(0);
for(ScheduleElement alarmCandidate : alarmCandidates)
{
if(Math.abs(currentTime - alarmCandidate.time.getTimeInMillis()) < Math.abs(currentTime - scheduleCandidate.time.getTimeInMillis()))
scheduleCandidate = alarmCandidate;
}
}
Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate.time.getTimeInMillis(), alarmPendingIntent);
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(scheduleCandidate.time.getTimeInMillis());
Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4);
}
public static void clearAlarms()
{
Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
for(int requestCode : requestCodeList)
{
Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0);
// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode));
centralAlarmManagerInstance.cancel(alarmPendingIntent);
}
requestCodeList.clear();
}
private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
{
if(!alarmListenerActive)
{
Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
DateTimeListener.automationServiceRef = givenAutomationServiceRef;
centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
// alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0);
alarmListenerActive = true;
Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
DateTimeListener.setAlarms();
// // get a Calendar object with current time
// Calendar cal = Calendar.getInstance();
// // add 5 minutes to the calendar object
// cal.add(Calendar.SECOND, 10);
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent);
}
else
Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
}
private static void stopAlarmListenerInternal()
{
if(alarmListenerActive)
{
Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
clearAlarms();
// centralAlarmManagerInstance.cancel(alarmPendingIntent);
alarmListenerActive = false;
}
else
Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
}
public static void reloadAlarms()
{
DateTimeListener.setAlarms();
}
@Override
public void startListener(AutomationService automationService)
{
DateTimeListener.startAlarmListener(automationService);
}
@Override
public void stopListener(AutomationService automationService)
{
DateTimeListener.stopAlarmListener(automationService);
}
public static boolean haveAllPermission()
{
return true;
}
@Override
public boolean isListenerRunning()
{
return isAlarmListenerActive();
}
@Override
public Trigger_Enum[] getMonitoredTrigger()
{
return new Trigger_Enum[] { Trigger_Enum.timeFrame };
}
static class ScheduleElement implements Comparable<ScheduleElement>
{
Calendar time;
String reason;
public ScheduleElement(Calendar timestamp, String reason)
{
super();
this.time = timestamp;
this.reason = reason;
}
@Override
public int compareTo(ScheduleElement o)
{
if(time.getTimeInMillis() == o.time.getTimeInMillis())
return 0;
if(time.getTimeInMillis() < o.time.getTimeInMillis())
return -1;
else
return 1;
}
@Override
public String toString()
{
return Miscellaneous.formatDate(time.getTime()) + ", reason : " + reason;
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now)
{
Calendar calSet;
Time setTime;
TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2());
if(tf.getRepetition() > 0)
{
if(trigger.getTriggerParameter())
setTime = tf.getTriggerTimeStart();
else
setTime = tf.getTriggerTimeStop();
calSet = (Calendar) now.clone();
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
calSet.set(Calendar.MINUTE, setTime.getMinutes());
calSet.set(Calendar.SECOND, 0);
calSet.set(Calendar.MILLISECOND, 0);
// if(this.applies(null))
// {
// If the starting time is a day ahead remove 1 day.
if(calSet.getTimeInMillis() > now.getTimeInMillis())
calSet.add(Calendar.DAY_OF_MONTH, -1);
long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000;
long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition());
Calendar calSchedule = Calendar.getInstance();
calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000);
/*
* Das war mal aktiviert. Allerdings: Die ganze Funktion liefert zurück, wenn die Regel NOCH nicht
* zutrifft, aber wir z.B. gleich den zeitlichen Bereich betreten.
*/
// if(trigger.checkDateTime(calSchedule.getTime(), false))
// {
return calSchedule;
// }
// }
}
else
Miscellaneous.logEvent("i", "DateTimeListener", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5);
return null;
}
}

View File

@ -0,0 +1,251 @@
package com.jens.automation2.receivers;
import static android.content.Context.SENSOR_SERVICE;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import com.jens.automation2.ActivityManageTriggerDeviceOrientation;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.Rule;
import com.jens.automation2.Settings;
import com.jens.automation2.Trigger;
import java.util.ArrayList;
import java.util.Calendar;
public class DeviceOrientationListener implements SensorEventListener, AutomationListenerInterface
{
// https://developer.android.com/guide/topics/sensors/sensors_position#java
ActivityManageTriggerDeviceOrientation activityManageTriggerDeviceOrientationInstance = null;
//the Sensor Manager
private SensorManager sManager;
static DeviceOrientationListener instance = null;
boolean isRunning = false;
Calendar now = null;
static Calendar lastTimeSignalArrived = null;
static int sensorValueCounter = 0;
// Gravity rotational data
private float gravity[];
// Magnetic rotational data
private float magnetic[]; //for magnetic rotational data
private float accels[] = new float[3];
private float mags[] = new float[3];
private float[] values = new float[3];
// azimuth, pitch and roll
private float azimuth;
private float pitch;
private float roll;
boolean applies = false;
boolean flipped = false;
boolean toggable = false;
public static DeviceOrientationListener getInstance()
{
if (instance == null)
instance = new DeviceOrientationListener();
return instance;
}
public float getAzimuth()
{
return azimuth;
}
public float getPitch()
{
return pitch;
}
public float getRoll()
{
return roll;
}
public void startSensorFromConfigActivity(Context context, ActivityManageTriggerDeviceOrientation activityManageTriggerDeviceOrientationInstance)
{
this.activityManageTriggerDeviceOrientationInstance = activityManageTriggerDeviceOrientationInstance;
if(!isRunning)
{
sManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
/*
register the sensor listener to listen to the gyroscope sensor, use the
callbacks defined in this class, and gather the sensor information as quick
as possible
*/
isRunning = true;
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL);
}
}
public void stopSensorFromConfigActivity()
{
activityManageTriggerDeviceOrientationInstance = null;
if(isRunning)
{
if(!Rule.isAnyRuleUsing(Trigger.Trigger_Enum.deviceOrientation))
{
//unregister the sensor listener
sManager.unregisterListener(this);
isRunning = false;
}
}
}
@Override
public void onAccuracyChanged(Sensor arg0, int arg1)
{
//Do nothing.
}
@Override
public void onSensorChanged(SensorEvent event)
{
switch (event.sensor.getType())
{
case Sensor.TYPE_MAGNETIC_FIELD:
mags = event.values.clone();
break;
case Sensor.TYPE_ACCELEROMETER:
accels = event.values.clone();
break;
}
if (mags != null && accels != null)
{
gravity = new float[9];
magnetic = new float[9];
SensorManager.getRotationMatrix(gravity, magnetic, accels, mags);
float[] outGravity = new float[9];
SensorManager.remapCoordinateSystem(gravity, SensorManager.AXIS_X, SensorManager.AXIS_Z, outGravity);
SensorManager.getOrientation(outGravity, values);
azimuth = values[0] * 57.2957795f;
pitch = values[1] * 57.2957795f;
roll = values[2] * 57.2957795f;
mags = null;
accels = null;
}
//else it will output the Roll, Pitch and Yawn values
if(activityManageTriggerDeviceOrientationInstance != null)
activityManageTriggerDeviceOrientationInstance.updateFields(azimuth, pitch, roll);
/*
For some reason the first 3 values after starting the listener
are crap.
*/
if(sensorValueCounter > 3)
{
now = Calendar.getInstance();
if (lastTimeSignalArrived == null || now.getTimeInMillis() >= lastTimeSignalArrived.getTimeInMillis() + Settings.acceptDeviceOrientationSignalEveryX_MilliSeconds)
{
lastTimeSignalArrived = now;
Miscellaneous.logEvent("i", "DeviceOrientation", "Got device orientation update: azimuth: " + String.valueOf(azimuth) + ", pitch: " + String.valueOf(pitch) + ", roll: " + String.valueOf(pitch), 4);
if (AutomationService.isMyServiceRunning(Miscellaneous.getAnyContext()))
{
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.deviceOrientation);
for (int i = 0; i < ruleCandidates.size(); i++)
{
if (ruleCandidates.get(i).getsGreenLight(Miscellaneous.getAnyContext()))
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
}
}
}
else
sensorValueCounter++;
}
@Override
public void startListener(AutomationService automationService)
{
if(!isRunning)
{
sManager = (SensorManager) Miscellaneous.getAnyContext().getSystemService(SENSOR_SERVICE);
/*
register the sensor listener to listen to the gyroscope sensor, use the
callbacks defined in this class, and gather the sensor information as quick
as possible
*/
isRunning = true;
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL);
}
}
@Override
public void stopListener(AutomationService automationService)
{
this.activityManageTriggerDeviceOrientationInstance = null;
if(isRunning)
{
//unregister the sensor listener
sManager.unregisterListener(this);
isRunning = false;
}
}
@Override
public boolean isListenerRunning()
{
return isRunning;
}
@Override
public Trigger.Trigger_Enum[] getMonitoredTrigger()
{
return new Trigger.Trigger_Enum[] { Trigger.Trigger_Enum.deviceOrientation};
}
/*
Azimuth (degrees of rotation about the -z axis).
This is the angle between the device's current compass direction and magnetic north. If the top edge of the
device faces magnetic north, the azimuth is 0 degrees; if the top edge faces south, the azimuth is 180 degrees.
Similarly, if the top edge faces east, the azimuth is 90 degrees, and if the top edge faces west, the azimuth is 270 degrees.
Pitch (degrees of rotation about the x axis).
This is the angle between a plane parallel to the device's screen and a plane parallel to the ground. If you hold the device
parallel to the ground with the bottom edge closest to you and tilt the top edge of the device toward the ground, the pitch
angle becomes positive. Tilting in the opposite direction— moving the top edge of the device away from the ground—causes
the pitch angle to become negative. The range of values is -180 degrees to 180 degrees.
Roll (degrees of rotation about the y axis).
This is the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground.
If you hold the device parallel to the ground with the bottom edge closest to you and tilt the left edge of the
device toward the ground, the roll angle becomes positive. Tilting in the opposite direction—moving the right
edge of the device toward the ground— causes the roll angle to become negative. The range of values is -90 degrees
to 90 degrees.
Computes the device's orientation based on the rotation matrix.
When it returns, the array values are as follows:
values[0]: Azimuth, angle of rotation about the -z axis. This value represents the angle between the device's y axis and the magnetic north pole. When facing north, this angle is 0, when facing south, this angle is π. Likewise, when facing east, this angle is π/2, and when facing west, this angle is -π/2. The range of values is -π to π.
values[1]: Pitch, angle of rotation about the x axis. This value represents the angle between a plane parallel to the device's screen and a plane parallel to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the top edge of the device toward the ground creates a positive pitch angle. The range of values is -π to π.
values[2]: Roll, angle of rotation about the y axis. This value represents the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the left edge of the device toward the ground creates a positive roll angle. The range of values is -π/2 to π/2.
Applying these three rotations in the azimuth, pitch, roll order transforms an identity matrix to the rotation matrix passed into this method. Also, note that all three orientation angles are expressed in radians.
*/
}

View File

@ -1,5 +1,6 @@
package com.jens.automation2.receivers;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -73,11 +74,12 @@ public class HeadphoneJackListener extends BroadcastReceiver implements Automati
headsetConnected = true;
Miscellaneous.logEvent("i", "HeadphoneJackListener", "Headset " + name + " plugged in.", 4);
}
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByHeadphoneJack(isHeadsetConnected());
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.headsetPlugged);
// ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByHeadphoneJack(isHeadsetConnected());
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(context))
if(ruleCandidates.get(i).getsGreenLight(context))
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
}
@ -133,7 +135,7 @@ public class HeadphoneJackListener extends BroadcastReceiver implements Automati
public static boolean haveAllPermission()
{
return ActivityPermissions.havePermission("android.permission.READ_PHONE_STATE", Miscellaneous.getAnyContext());
return ActivityPermissions.havePermission(Manifest.permission.READ_PHONE_STATE, Miscellaneous.getAnyContext());
}

View File

@ -19,6 +19,7 @@ import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.R;
import com.jens.automation2.Rule;
import com.jens.automation2.Trigger;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@ -171,10 +172,10 @@ public class NfcReceiver
}
else
{
ArrayList<Rule> allRulesWithNfcTags = Rule.findRuleCandidatesByNfc();
ArrayList<Rule> allRulesWithNfcTags = Rule.findRuleCandidates(Trigger.Trigger_Enum.nfcTag);
for(int i=0; i<allRulesWithNfcTags.size(); i++)
{
if(allRulesWithNfcTags.get(i).applies(asInstance))
if(allRulesWithNfcTags.get(i).getsGreenLight(asInstance))
allRulesWithNfcTags.get(i).activate(asInstance, false);
}
}

View File

@ -30,10 +30,10 @@ public class NoiseListener implements AutomationListenerInterface
noiseLevelDb = msg.getData().getLong("noiseLevelDb");
// execute matching rules containing noise
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByNoiseLevel();
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.noiseLevel);
for(Rule oneRule : ruleCandidates)
{
if(oneRule.applies(automationService))
if(oneRule.getsGreenLight(automationService))
oneRule.activate(automationService, false);
}
}

View File

@ -0,0 +1,171 @@
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;
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification);
for (int i = 0; i < ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).getsGreenLight(NotificationListener.this))
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
}
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

@ -14,29 +14,18 @@ public class PackageReplacedReceiver extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
{
// Toast.makeText(context, "package replaced", Toast.LENGTH_LONG).show();
// int intentUid = intent.getExtras().getInt("android.intent.extra.UID"); // userid of the application that has just been updated
// int myUid = android.os.Process.myUid(); // userid of this application
//
// boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
// if(intentUid == myUid)
// {
Settings.readFromPersistentStorage(context);
Settings.readFromPersistentStorage(context);
Miscellaneous.logEvent("i", context.getResources().getString(R.string.applicationHasBeenUpdated), context.getResources().getString(R.string.applicationHasBeenUpdated), 2);
if(hasServiceBeenRunning() && Settings.startServiceAfterAppUpdate)
{
Miscellaneous.logEvent("i", "Service", context.getResources().getString(R.string.logStartingServiceAfterAppUpdate), 1);
AutomationService.startAutomationService(context, true);
}
else
{
Miscellaneous.logEvent("i", "Service", context.getResources().getString(R.string.logNotStartingServiceAfterAppUpdate), 2);
}
// }
// else
// Miscellaneous.logEvent("i", "Service", "Some other app has been updated.", 5);
Miscellaneous.logEvent("i", context.getResources().getString(R.string.applicationHasBeenUpdated), context.getResources().getString(R.string.applicationHasBeenUpdated), 2);
if(hasServiceBeenRunning() && Settings.startServiceAfterAppUpdate)
{
Miscellaneous.logEvent("i", "Service", context.getResources().getString(R.string.logStartingServiceAfterAppUpdate), 1);
AutomationService.startAutomationService(context, true);
}
else
{
Miscellaneous.logEvent("i", "Service", context.getResources().getString(R.string.logNotStartingServiceAfterAppUpdate), 2);
}
}
private static boolean hasServiceBeenRunning()

View File

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

View File

@ -43,34 +43,27 @@ public class ProcessListener implements AutomationListenerInterface
@Override
public void handleMessage(Message msg)
{
// try
// {
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.processMonitoring), automationService.getResources().getString(R.string.messageReceivedStatingProcessMonitoringIsComplete), 5);
// This will take care of results delivered by the actual monitoring instance
for(String entry : getRunningApps())
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.runningApp), entry, 5);
// execute matching rules containing processes
if(getRecentlyStartedApps().size()>0 | getRecentlyStoppedApps().size()>0)
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.processMonitoring), automationService.getResources().getString(R.string.messageReceivedStatingProcessMonitoringIsComplete), 5);
// This will take care of results delivered by the actual monitoring instance
for(String entry : getRunningApps())
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.runningApp), entry, 5);
// execute matching rules containing processes
if(getRecentlyStartedApps().size()>0 | getRecentlyStoppedApps().size()>0)
{
for(String entry : getRecentlyStartedApps())
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStarted), entry, 3);
for(String entry : getRecentlyStoppedApps())
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStopped), entry, 3);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.process_started_stopped);
for(int i=0; i<ruleCandidates.size(); i++)
{
for(String entry : getRecentlyStartedApps())
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStarted), entry, 3);
for(String entry : getRecentlyStoppedApps())
Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStopped), entry, 3);
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByProcess();
for(int i=0; i<ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).applies(automationService))
ruleCandidates.get(i).activate(automationService, false);
}
if(ruleCandidates.get(i).getsGreenLight(automationService))
ruleCandidates.get(i).activate(automationService, false);
}
// }
// catch(Exception e)
// {
// Miscellaneous.logEvent("e", "Noise level", "Error in workHandler->handleMessage(): " + e.getMessage());
// }
}
}
};

View File

@ -77,12 +77,12 @@ public class TimeZoneListener extends BroadcastReceiver implements AutomationLis
if(action.equals(Intent.ACTION_TIMEZONE_CHANGED))
{
Miscellaneous.logEvent("i", "TimeZoneListener", "Device timezone changed. Reloading alarms.", 3);
AlarmListener.reloadAlarms();
DateTimeListener.reloadAlarms();
}
else if(action.equals(Intent.ACTION_TIME_CHANGED))
{
Miscellaneous.logEvent("i", "TimeZoneListener", "Device time changed. Reloading alarms.", 4);
AlarmListener.reloadAlarms();
DateTimeListener.reloadAlarms();
}
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

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

View File

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

View File

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

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