Compare commits
415 Commits
v2.0.5-bet
...
patch-deve
Author | SHA1 | Date | |
---|---|---|---|
cadc881ad8 | |||
f25ce937ce | |||
564962dc9c | |||
f893748941 | |||
64ff404eac | |||
b95a9e8e7f | |||
1667d9c22c | |||
fb3f28d035 | |||
d8e204c5d9 | |||
388653f62b | |||
5763a18421 | |||
5dcf0afe02 | |||
94b1eec757 | |||
c5b41d0886 | |||
4e5bf62e23 | |||
77864cbef4 | |||
32c580bac7 | |||
85fa0cae11 | |||
05f2a745c2 | |||
ef2e89b6ff | |||
a306d92282 | |||
da9fc362af | |||
ff83cfd953 | |||
5d9dcd9701 | |||
183901534c | |||
6d7d90e762 | |||
e89b377b68 | |||
e0eb6f77da | |||
43c08204c3 | |||
6f125573e0 | |||
380dc96c40 | |||
821980e938 | |||
9f47d626a9 | |||
d3b623cf13 | |||
1389ddbfc0 | |||
0dbbe0e5d2 | |||
818b4ec0ba | |||
8c84913cd8 | |||
43f085b13c | |||
3ab42fd163 | |||
ea0be72478 | |||
c744d3c7f8 | |||
7b93548b0b | |||
260e36b305 | |||
00af2159ea | |||
c26021de03 | |||
218dae8cc2 | |||
b3e2d8d843 | |||
80d1077dab | |||
dd2a74aaf6 | |||
fb1953d513 | |||
b081b9adbb | |||
fd398faf42 | |||
e14662c534 | |||
4224562512 | |||
f13d426831 | |||
5dc7a1b30d | |||
6538cdebc2 | |||
78709ed018 | |||
c0e87047c2 | |||
1a2c97d61f | |||
4335751749 | |||
9bbc816bea | |||
e8a6743dc4 | |||
5d8ceb98cc | |||
dfff4c5e1b | |||
13f8814480 | |||
8608f9adf3 | |||
ad65cf0e84 | |||
1ecaf7a11a | |||
2578566659 | |||
61fc0da8d0 | |||
285b754dd5 | |||
194a2ab456 | |||
bca22d5aa8 | |||
35536a89a0 | |||
6780a470e9 | |||
6ca6b5ab95 | |||
7edb0635a7 | |||
d72ddd6d85 | |||
5d07cc8d73 | |||
1ac53e09a8 | |||
3412e044df | |||
80023da430 | |||
e2a2d17506 | |||
b93443b736 | |||
9842ba3ea9 | |||
75aba66987 | |||
f325af26f8 | |||
b61fbd193c | |||
03d9446369 | |||
23f94e63c6 | |||
01775d3a3a | |||
67abd14bb1 | |||
854cfac28c | |||
8fafe591e8 | |||
a85fefe4dc | |||
974185a89b | |||
bbe1497f8b | |||
1f22426dec | |||
40644f3657 | |||
a01c8eff63 | |||
1ee25bcc89 | |||
1bd18ac486 | |||
d24ac198a4 | |||
81578f5f4a | |||
8234a87a2a | |||
0774c6bdbe | |||
57eecd630d | |||
cce86a970c | |||
6ea40a51fe | |||
1721dff3cf | |||
c389d50b09 | |||
0ea55db4b1 | |||
3fba50fd2c | |||
06583197c7 | |||
5176331e84 | |||
65f83caeb5 | |||
60e8c267bf | |||
10a3204808 | |||
98c509ef27 | |||
ab1df499af | |||
ed9a4042c8 | |||
a102776214 | |||
0778ad4df5 | |||
8dce8a74b3 | |||
24bb811f93 | |||
0500e8d8e8 | |||
d2087d094f | |||
34fb35f2ab | |||
108ecdece0 | |||
e3f4995e93 | |||
0cdc5a3c6d | |||
9f039eec3c | |||
e9effbe799 | |||
889783bb4e | |||
d32f680519 | |||
c1d14f93bf | |||
b903fff10f | |||
20c5ce61b4 | |||
5bb81772f4 | |||
526a9ac6ac | |||
98db1380b7 | |||
ce9b343e0e | |||
21ec3c3f5d | |||
88950a84b4 | |||
fb853975e0 | |||
329eee6339 | |||
c595168320 | |||
61e3e43fd7 | |||
1513b96313 | |||
1cc5558d9e | |||
e12e908496 | |||
2aed9e3b25 | |||
536ed64d41 | |||
6b6ec633ee | |||
5b2d245e80 | |||
0aec34dcd2 | |||
b2a9b9fbb3 | |||
768f04825e | |||
80ee877bf2 | |||
ac7839ecdc | |||
66d0214cd9 | |||
e069b8f6ab | |||
8a681f0cd7 | |||
92158ec5f2 | |||
ebb37f21ed | |||
2a389cb422 | |||
4504a0617e | |||
9ed34ee17e | |||
a7294edfd4 | |||
49ca17803e | |||
97ab72081d | |||
e6fee0dfe1 | |||
e0c4f24c43 | |||
2a7d0f171b | |||
00dcfc3149 | |||
eb11b603aa | |||
4d2f624448 | |||
7581af4dd2 | |||
0325af6582 | |||
4de0413a35 | |||
c54a24c889 | |||
98eccd2833 | |||
0119a20765 | |||
a1e54892fd | |||
17801fd164 | |||
e6087b2969 | |||
59b8ba26a2 | |||
f190ee5d15 | |||
2f266ffcf8 | |||
0e376aba80 | |||
313e4fa92d | |||
a8ec754bc4 | |||
f8d1188634 | |||
8216fc96b9 | |||
56d95c5559 | |||
c0e747a714 | |||
16076dc145 | |||
c95f9fb943 | |||
331d5772af | |||
e2719b6445 | |||
bd35022f7d | |||
6d9cb750a7 | |||
f85b4c9a6a | |||
df54a4a79e | |||
6ea97e7724 | |||
d27071739d | |||
738781225f | |||
12d32f852b | |||
f0baa60363 | |||
785eb39334 | |||
d289699d08 | |||
c68d0a8327 | |||
de2e223713 | |||
6150dd7e22 | |||
d9ecebe770 | |||
52327715b1 | |||
e301e6e6ec | |||
c884fae362 | |||
eb78257e52 | |||
109aa24af1 | |||
2403f066a3 | |||
9bc7d7b62e | |||
a1a6f9f607 | |||
815a88a079 | |||
1c1f55e20a | |||
5259a81cfb | |||
3b963dae1c | |||
878ddcb05e | |||
0ce446f0ef | |||
c5fefb0e06 | |||
c5eb5358aa | |||
c4a16224f0 | |||
122c0627d9 | |||
6d80ec97a8 | |||
1c7df585fe | |||
9f4cdc950b | |||
0a0c5a90a7 | |||
011517e12d | |||
fdd82e0f91 | |||
abafe108c6 | |||
03f08e4f08 | |||
9ecb9b4819 | |||
2bb30aae69 | |||
01d219d38c | |||
6c7831d972 | |||
4ab12e33c7 | |||
364198ef08 | |||
2c81c7cfd2 | |||
3b723e5a1b | |||
6a912ee003 | |||
1644fb7682 | |||
bd4869540b | |||
31103303be | |||
e9c0cdd61c | |||
59e42029e8 | |||
b2ef2adb2a | |||
bab92f9169 | |||
cb480ed4ee | |||
6f83a45865 | |||
9e528e1f6f | |||
20d3ae0e32 | |||
ecea1265e7 | |||
c38b7a335c | |||
6ab8e40d45 | |||
d14dfee980 | |||
0a454f0b5f | |||
b7bc93e174 | |||
1dee4cc8e5 | |||
02b521c0b8 | |||
c04040e242 | |||
a208aa97b2 | |||
e3fda9457e | |||
b84631a2b2 | |||
bad06c5762 | |||
74d8966f0a | |||
d8a76936a6 | |||
e72ca4fc6f | |||
dd62212b06 | |||
8dfc12e412 | |||
2762f12151 | |||
a873c71918 | |||
e8f3c110a8 | |||
72bf3b04a7 | |||
ca6fb75dfd | |||
59dc5de21a | |||
ec40a277d7 | |||
507b7f2318 | |||
1a709a9406 | |||
770ba0cd13 | |||
6a92225edf | |||
ce801d4a7d | |||
25da807bd5 | |||
63212d13b1 | |||
79ad9b2500 | |||
9c0aa0a9ef | |||
b9197ddf2e | |||
d1362f7d81 | |||
a8c7d115ae | |||
a5a753e198 | |||
39ede20518 | |||
5527e6e405 | |||
565ed11f53 | |||
4bd8653d32 | |||
d121119ca9 | |||
e82a34bcc7 | |||
8867a1fbbd | |||
7265883d6c | |||
7a4fc6ff58 | |||
9f61215caa | |||
7ccf7eb8f6 | |||
704448a848 | |||
fa366b3d45 | |||
e58fef66aa | |||
94825808f4 | |||
c120bad9c6 | |||
0d2287dbdf | |||
9e40586456 | |||
4d75f4ca0c | |||
0859632803 | |||
47562b35ca | |||
0f4f02ea28 | |||
31cf950eee | |||
f230d300ee | |||
c610857056 | |||
770040ad93 | |||
f784817296 | |||
ec1c25cb4c | |||
e1d2f5a782 | |||
6e8c6cf055 | |||
68b5997e8d | |||
56b21be946 | |||
fdc02b2cef | |||
863b8f79d8 | |||
857a8009b6 | |||
9199f28ad9 | |||
ddb1b7494a | |||
fd2b1ba976 | |||
90f0d7de00 | |||
66ad5e0839 | |||
2ac7e072e5 | |||
a3392dabcf | |||
c2c54a04d2 | |||
2c237058b3 | |||
aa2b4b0510 | |||
4c27d1dd9b | |||
eba5575ee2 | |||
aff7e407ca | |||
f7fc31d968 | |||
90b588603d | |||
5d9bd11abb | |||
01005ec443 | |||
eaf6400e8b | |||
30339c7375 | |||
87da284be4 | |||
573c6d03e5 | |||
79e87b0648 | |||
9e9a91690e | |||
5f699af509 | |||
aae40b9dd3 | |||
cebd679856 | |||
06443ddddb | |||
233761a169 | |||
b81461f725 | |||
26b1948b70 | |||
af64818dff | |||
b04b103634 | |||
1dc050e77f | |||
ff171d4022 | |||
d91471d1ee | |||
ac381c8542 | |||
ba5a860e75 | |||
09bc9df22f | |||
385806413e | |||
7d2ea5a4d8 | |||
ce9f5a6e45 | |||
8f0f6bc868 | |||
34d8fa4b59 | |||
9401b89036 | |||
842c0d764f | |||
1754b4045b | |||
f28596c194 | |||
7f41a92a91 | |||
9a63b9bde2 | |||
26428b65da | |||
97d1caeabc | |||
f013be5a7a | |||
4cc55edb15 | |||
d08ad6171e | |||
9e2eacef73 | |||
f8f8a8f051 | |||
60531061d7 | |||
654ec3fe66 | |||
40c9253159 | |||
d86d2cadd4 | |||
cb6c2c7764 | |||
6a4cfdf22b | |||
0b9e9e081e | |||
15884bd9b2 | |||
0adf192965 | |||
d72ba08960 | |||
1848951a74 | |||
5ca06e817e | |||
37cb43dc80 | |||
ea372dd76d | |||
7076311e94 | |||
2e684e0902 | |||
e2503decd2 | |||
f13cee24d5 | |||
f5c4ee7eb0 | |||
4feb72381d | |||
bea0803c3a | |||
dd450443c8 | |||
6279846cea |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: ['paypal.me/tommasoberlose']
|
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: tommasoberlose
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. OnePlus 6]
|
||||||
|
- OS Version: [e.g. Android 9.0]
|
||||||
|
- App Version (that you can find inside the advanced settings) [e.g. v2.0.5 (71)]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: tommasoberlose
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,9 +1,14 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
/local.properties
|
||||||
/.idea/workspace.xml
|
/.idea/*
|
||||||
/.idea/libraries
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
|
/tasksintegration/build
|
||||||
|
/app/google-services.json
|
||||||
|
apikey.properties
|
||||||
|
./.idea/*
|
||||||
|
app/release/*
|
||||||
|
/app/release/*
|
203
.idea/assetWizardSettings.xml
generated
203
.idea/assetWizardSettings.xml
generated
@ -19,6 +19,29 @@
|
|||||||
<option name="children">
|
<option name="children">
|
||||||
<map>
|
<map>
|
||||||
<entry key="clipArt">
|
<entry key="clipArt">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="text">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="textAsset">
|
||||||
<value>
|
<value>
|
||||||
<PersistentState>
|
<PersistentState>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
@ -45,14 +68,47 @@
|
|||||||
<PersistentState>
|
<PersistentState>
|
||||||
<option name="children">
|
<option name="children">
|
||||||
<map>
|
<map>
|
||||||
|
<entry key="foregroundClipArt">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
<entry key="foregroundImage">
|
<entry key="foregroundImage">
|
||||||
<value>
|
<value>
|
||||||
<PersistentState>
|
<PersistentState>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<map>
|
||||||
<entry key="color" value="000000" />
|
<entry key="color" value="000000" />
|
||||||
|
<entry key="imagePath" value="$USER_HOME$/Desktop/logo-white.png" />
|
||||||
<entry key="scalingPercent" value="70" />
|
<entry key="scalingPercent" value="70" />
|
||||||
<entry key="trimmed" value="true" />
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="foregroundText">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="foregroundTextAsset">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</PersistentState>
|
</PersistentState>
|
||||||
@ -64,7 +120,6 @@
|
|||||||
<map>
|
<map>
|
||||||
<entry key="backgroundAssetType" value="COLOR" />
|
<entry key="backgroundAssetType" value="COLOR" />
|
||||||
<entry key="backgroundColor" value="ffffff" />
|
<entry key="backgroundColor" value="ffffff" />
|
||||||
<entry key="foregroundImage" value="$USER_HOME$/Desktop/Artboard Copy 3.png" />
|
|
||||||
<entry key="legacyIconShape" value="CIRCLE" />
|
<entry key="legacyIconShape" value="CIRCLE" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
@ -77,6 +132,29 @@
|
|||||||
<option name="children">
|
<option name="children">
|
||||||
<map>
|
<map>
|
||||||
<entry key="clipArt">
|
<entry key="clipArt">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="text">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="textAsset">
|
||||||
<value>
|
<value>
|
||||||
<PersistentState>
|
<PersistentState>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
@ -98,6 +176,104 @@
|
|||||||
<option name="children">
|
<option name="children">
|
||||||
<map>
|
<map>
|
||||||
<entry key="clipArt">
|
<entry key="clipArt">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="text">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="textAsset">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="tvBanner">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="foregroundText">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="tvChannel">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="foregroundClipArt">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="foregroundImage">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="foregroundText">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="color" value="000000" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="foregroundTextAsset">
|
||||||
<value>
|
<value>
|
||||||
<PersistentState>
|
<PersistentState>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
@ -123,6 +299,29 @@
|
|||||||
</PersistentState>
|
</PersistentState>
|
||||||
</value>
|
</value>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry key="vectorWizard">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="vectorAssetStep">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="assetSourceType" value="FILE" />
|
||||||
|
<entry key="outputName" value="round_aspect_ratio_24" />
|
||||||
|
<entry key="sourceFile" value="$USER_HOME$/Desktop/round_aspect_ratio_24.svg" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
16
.idea/codeStyles/Project.xml
generated
16
.idea/codeStyles/Project.xml
generated
@ -1,6 +1,22 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
|
<value>
|
||||||
|
<package name="java.util" alias="false" withSubpackages="false" />
|
||||||
|
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||||
|
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||||
|
<value>
|
||||||
|
<package name="" alias="false" withSubpackages="true" />
|
||||||
|
<package name="java" alias="false" withSubpackages="true" />
|
||||||
|
<package name="javax" alias="false" withSubpackages="true" />
|
||||||
|
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||||
|
<package name="" alias="true" withSubpackages="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
|
5
.idea/gradle.xml
generated
5
.idea/gradle.xml
generated
@ -4,17 +4,16 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="testRunner" value="PLATFORM" />
|
<option name="testRunner" value="GRADLE" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="Embedded JDK" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
<option value="$PROJECT_DIR$/tasksintegration" />
|
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -61,7 +61,7 @@
|
|||||||
</profile-state>
|
</profile-state>
|
||||||
</entry>
|
</entry>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -2,9 +2,11 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/Another Widget.iml" filepath="$PROJECT_DIR$/Another Widget.iml" group="Another Widget" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Another_Widget.iml" filepath="$PROJECT_DIR$/.idea/modules/Another_Widget.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="Another Widget/app" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/tasksintegration/tasksintegration.iml" filepath="$PROJECT_DIR$/tasksintegration/tasksintegration.iml" group="Another Widget/tasksintegration" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.androidTest.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.main.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.main.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.unitTest.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
192
.idea/navEditor.xml
generated
Normal file
192
.idea/navEditor.xml
generated
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="navEditor-manualLayoutAlgorithm2">
|
||||||
|
<option name="myPositions">
|
||||||
|
<map>
|
||||||
|
<entry key="nav_graph.xml">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPositions">
|
||||||
|
<map>
|
||||||
|
<entry key="appMainFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="242" />
|
||||||
|
<option name="y" value="352" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
<option name="myPositions">
|
||||||
|
<map>
|
||||||
|
<entry key="action_appMainFragment_to_appSettingsFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="appSettingsFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="532" />
|
||||||
|
<option name="y" value="353" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="settings_nav_graph.xml">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPositions">
|
||||||
|
<map>
|
||||||
|
<entry key="calendarTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="190" />
|
||||||
|
<option name="y" value="4" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="clockTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="494" />
|
||||||
|
<option name="y" value="302" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="generalTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="-48" />
|
||||||
|
<option name="y" value="133" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="gesturesFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="703" />
|
||||||
|
<option name="y" value="14" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="glanceTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="2" />
|
||||||
|
<option name="y" value="-198" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="tabSelectorFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="-537" />
|
||||||
|
<option name="y" value="216" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
<option name="myPositions">
|
||||||
|
<map>
|
||||||
|
<entry key="action_tabSelectorFragment_to_calendarTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="action_tabSelectorFragment_to_clockTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="action_tabSelectorFragment_to_generalTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="action_tabSelectorFragment_to_glanceTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="action_tabSelectorFragment_to_typographyTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="action_tabSelectorFragment_to_weatherTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="typographyTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="470" />
|
||||||
|
<option name="y" value="-207" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="weatherTabFragment">
|
||||||
|
<value>
|
||||||
|
<LayoutPositions>
|
||||||
|
<option name="myPosition">
|
||||||
|
<Point>
|
||||||
|
<option name="x" value="-301" />
|
||||||
|
<option name="y" value="-160" />
|
||||||
|
</Point>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</LayoutPositions>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
12
.idea/runConfigurations.xml
generated
12
.idea/runConfigurations.xml
generated
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RunConfigurationProducerService">
|
|
||||||
<option name="ignoredProducers">
|
|
||||||
<set>
|
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -11,7 +11,14 @@ While respecting the design of the application, there is a great opportunity to
|
|||||||
Also, as much as possible, there are always updates and new features in the short run.
|
Also, as much as possible, there are always updates and new features in the short run.
|
||||||
|
|
||||||
Help me developing with feedback and support me on how you can.
|
Help me developing with feedback and support me on how you can.
|
||||||
<div style="text-align:center"><a href="https://play.google.com/store/apps/details?id=com.tommasoberlose.anotherwidget" target="_blank"><img src="google-play-badge.png" height="100" /></a></div>
|
<div style="text-align:center"><a href='https://play.google.com/store/apps/details?id=com.tommasoberlose.anotherwidget&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height='100px' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a></div>
|
||||||
|
|
||||||
|
|
||||||
|
Help with translations
|
||||||
|
-------
|
||||||
|
|
||||||
|
Hey! You could view the file strings.xml ([here](https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml)) that contains the English version of the app strings.
|
||||||
|
You have to copy the file, create a copy of it inside the folder values-[LANGUAGE-SUFFIX] with the translated strings and create a pull request to submit your changes.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
@ -1,27 +1,22 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'com.google.gms.google-services'
|
|
||||||
|
|
||||||
// Apply the Crashlytics Gradle plugin
|
|
||||||
apply plugin: 'com.google.firebase.crashlytics'
|
|
||||||
|
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
|
||||||
|
|
||||||
apply plugin: 'realm-android'
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
|
||||||
buildToolsVersion "29.0.3"
|
compileSdkVersion 30
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.tommasoberlose.anotherwidget"
|
applicationId "com.tommasoberlose.anotherwidget"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 29
|
targetSdkVersion 30
|
||||||
versionCode 59
|
versionCode 139
|
||||||
versionName "2.0.5"
|
versionName "2.3.3"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
renderscriptSupportModeEnabled true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@ -47,79 +42,85 @@ android {
|
|||||||
packagingOptions {
|
packagingOptions {
|
||||||
exclude 'META-INF/DEPENDENCIES'
|
exclude 'META-INF/DEPENDENCIES'
|
||||||
}
|
}
|
||||||
dataBinding {
|
|
||||||
enabled = true
|
buildFeatures {
|
||||||
|
dataBinding = true
|
||||||
|
viewBinding = true
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.enabled = true
|
|
||||||
|
|
||||||
dynamicFeatures = [":tasksintegration"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
|
||||||
implementation 'androidx.core:core-ktx:1.2.0'
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
implementation 'com.google.android.material:material:1.2.0-alpha06'
|
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||||
implementation 'androidx.browser:browser:1.2.0'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
|
implementation 'androidx.browser:browser:1.3.0'
|
||||||
implementation 'net.idik:slimadapter:2.1.2'
|
implementation 'net.idik:slimadapter:2.1.2'
|
||||||
implementation 'com.google.android:flexbox:2.0.1'
|
implementation 'com.google.android:flexbox:2.0.1'
|
||||||
|
implementation 'com.kyleduo.switchbutton:library:2.0.3'
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
|
|
||||||
|
implementation "androidx.work:work-runtime-ktx:2.5.0"
|
||||||
|
|
||||||
// EventBus
|
// EventBus
|
||||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||||
|
|
||||||
|
// Room
|
||||||
|
implementation "androidx.room:room-runtime:2.3.0"
|
||||||
|
kapt "androidx.room:room-compiler:2.3.0"
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
implementation 'androidx.navigation:navigation-fragment:2.3.0-alpha05'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation 'joda-time:joda-time:2.9.9'
|
implementation 'net.danlew:android.joda:2.10.9'
|
||||||
implementation 'me.everything:providers-android:1.0.1'
|
implementation 'me.everything:providers-android:1.0.1'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2'
|
||||||
|
|
||||||
|
//Glide
|
||||||
|
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
||||||
|
|
||||||
//Weather
|
//Weather
|
||||||
implementation 'com.github.KwabenBerko:OpenWeatherMap-Android-Library:2.0.2'
|
implementation 'com.github.KwabenBerko:OpenWeatherMap-Android-Library:2.0.2'
|
||||||
implementation 'com.google.android.gms:play-services-location:17.0.0'
|
|
||||||
|
|
||||||
// Billing
|
|
||||||
implementation 'com.android.billingclient:billing:2.2.0'
|
|
||||||
implementation 'com.android.billingclient:billing-ktx:2.2.0'
|
|
||||||
|
|
||||||
// KTX
|
// KTX
|
||||||
implementation "androidx.core:core-ktx:1.2.0"
|
implementation "androidx.core:core-ktx:1.5.0"
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
|
||||||
implementation "androidx.palette:palette-ktx:1.0.0"
|
implementation "androidx.palette:palette-ktx:1.0.0"
|
||||||
|
implementation 'androidx.core:core-ktx:1.5.0'
|
||||||
|
|
||||||
// Recommended: Add the Firebase SDK for Google Analytics.
|
//Retrofit
|
||||||
implementation 'com.google.firebase:firebase-analytics:17.4.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
|
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
|
||||||
|
implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1"
|
||||||
|
|
||||||
// Add the Firebase SDK for Crashlytics.
|
//Coroutines
|
||||||
implementation 'com.google.firebase:firebase-crashlytics:17.0.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
implementation 'com.chibatching.kotpref:kotpref:2.10.0'
|
implementation 'com.chibatching.kotpref:kotpref:2.13.1'
|
||||||
implementation 'com.chibatching.kotpref:livedata-support:2.10.0'
|
implementation 'com.chibatching.kotpref:livedata-support:2.13.1'
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
implementation 'com.karumi:dexter:6.1.0'
|
implementation 'com.karumi:dexter:6.2.1'
|
||||||
|
|
||||||
// Billing
|
// Fonts
|
||||||
implementation 'com.android.billingclient:billing:2.2.0'
|
implementation 'com.github.firatkarababa:downloadable-font-list-library:1.0.2'
|
||||||
implementation 'com.android.billingclient:billing-ktx:2.2.0'
|
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"project_info": {
|
|
||||||
"project_number": "791844924473",
|
|
||||||
"firebase_url": "https://anotherwidget-182008.firebaseio.com",
|
|
||||||
"project_id": "anotherwidget-182008",
|
|
||||||
"storage_bucket": "anotherwidget-182008.appspot.com"
|
|
||||||
},
|
|
||||||
"client": [
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:791844924473:android:0ad4f6e3890f1ad320b1e8",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.tommasoberlose.anotherwidget"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "791844924473-73dh46rorjq8vm97dgbn6can2dcpqlf0.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyAeJRXstqnzebibxmm3FRM98nbwE_kC8tA"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "791844924473-73dh46rorjq8vm97dgbn6can2dcpqlf0.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"configuration_version": "1"
|
|
||||||
}
|
|
Binary file not shown.
@ -1,13 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.tommasoberlose.anotherwidget">
|
package="com.tommasoberlose.anotherwidget">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="com.android.vending.BILLING" />
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||||
|
<uses-permission android:name="android.gms.permission.ACTIVITY_RECOGNITION"/>
|
||||||
|
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
||||||
|
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
|
||||||
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -16,26 +23,31 @@
|
|||||||
android:name=".AWApplication"
|
android:name=".AWApplication"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:usesCleartextTraffic="true"
|
||||||
<activity android:name=".ui.activities.MainActivity" android:launchMode="singleInstance" android:theme="@style/AppTheme.Main">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:ignore="LockedOrientationActivity">
|
||||||
|
<activity android:name=".ui.activities.SplashActivity" android:exported="true" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ui.activities.ChooseApplicationActivity" android:launchMode="singleInstance" />
|
<activity android:name=".ui.activities.MainActivity" android:theme="@style/AppTheme" android:screenOrientation="portrait" />
|
||||||
<activity android:name=".ui.activities.CustomLocationActivity" android:launchMode="singleInstance" />
|
<activity android:name=".ui.activities.tabs.ChooseApplicationActivity" android:screenOrientation="portrait" />
|
||||||
<activity android:name=".ui.activities.WeatherProviderActivity" android:launchMode="singleInstance" />
|
<activity android:name=".ui.activities.tabs.CustomFontActivity" android:screenOrientation="portrait" />
|
||||||
<activity android:name=".ui.activities.SupportDevActivity" android:launchMode="singleInstance" />
|
<activity android:name=".ui.activities.tabs.CustomLocationActivity" android:screenOrientation="portrait" />
|
||||||
<activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" />
|
<activity android:name=".ui.activities.tabs.WeatherProviderActivity" android:screenOrientation="portrait" />
|
||||||
|
<activity android:name=".ui.activities.tabs.CustomDateActivity" android:screenOrientation="portrait" />
|
||||||
|
<activity android:name=".ui.activities.settings.IntegrationsActivity" android:screenOrientation="portrait" />
|
||||||
|
<activity android:name=".ui.activities.tabs.MusicPlayersFilterActivity" android:screenOrientation="portrait" />
|
||||||
|
<activity android:name=".ui.activities.tabs.AppNotificationsFilterActivity" android:screenOrientation="portrait" />
|
||||||
|
<activity android:name=".ui.activities.tabs.MediaInfoFormatActivity" android:screenOrientation="portrait" />
|
||||||
|
<activity android:name=".ui.activities.tabs.TimeZoneSelectorActivity" android:screenOrientation="portrait" />
|
||||||
|
|
||||||
|
<receiver android:name=".ui.widgets.MainWidget" android:exported="true">
|
||||||
<receiver android:name=".ui.widgets.MainWidget">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/the_widget_info" />
|
android:resource="@xml/the_widget_info" />
|
||||||
@ -61,12 +73,15 @@
|
|||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_CALENDAR_UPDATE" />
|
<action android:name="com.tommasoberlose.anotherwidget.action.CALENDAR_UPDATE" />
|
||||||
|
<action android:name="com.tommasoberlose.anotherwidget.action.TIME_UPDATE" />
|
||||||
<action android:name="com.sec.android.widgetapp.APPWIDGET_RESIZE" />
|
<action android:name="com.sec.android.widgetapp.APPWIDGET_RESIZE" />
|
||||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
|
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
|
||||||
<action android:name="android.intent.action.DATE_CHANGED" />
|
<action android:name="android.intent.action.DATE_CHANGED" />
|
||||||
<action android:name="android.intent.action.TIME_SET" />
|
<action android:name="android.intent.action.TIME_SET" />
|
||||||
|
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
|
||||||
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
@ -74,25 +89,48 @@
|
|||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="com.tommasoberlose.anotherwidget.action.WEATHER_UPDATE" />
|
||||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
<action android:name="android.intent.action.TIME_SET" />
|
<action android:name="android.intent.action.TIME_SET" />
|
||||||
|
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
|
||||||
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".receivers.WidgetClickListenerReceiver"
|
android:name=".receivers.WidgetClickListenerReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_OPEN_WEATHER_INTENT" />
|
<action android:name="com.tommasoberlose.anotherwidget.action.OPEN_WEATHER_INTENT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<service android:name=".services.EventListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
|
<service android:name=".receivers.NotificationListener" android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.service.notification.NotificationListenerService" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<receiver android:name=".receivers.BatteryLevelReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||||
|
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||||
|
<action android:name="android.intent.action.BATTERY_LOW"/>
|
||||||
|
<action android:name="android.intent.action.BATTERY_OKAY"/>
|
||||||
|
<action android:name="android.intent.action.BATTERY_CHANGED"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<package android:name="com.google.android.apps.fitness"/>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Before Width: | Height: | Size: 45 KiB |
@ -3,46 +3,16 @@ package com.tommasoberlose.anotherwidget
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import com.chibatching.kotpref.Kotpref
|
import com.chibatching.kotpref.Kotpref
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
|
|
||||||
class AWApplication : Application() {
|
class AWApplication : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
// Firebase crashlitycs
|
|
||||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
|
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
Kotpref.init(this)
|
Kotpref.init(this)
|
||||||
|
|
||||||
// Dark theme
|
// Dark theme
|
||||||
AppCompatDelegate.setDefaultNightMode(Preferences.darkThemePreference)
|
AppCompatDelegate.setDefaultNightMode(Preferences.darkThemePreference)
|
||||||
|
|
||||||
// Realm
|
|
||||||
Realm.init(this)
|
|
||||||
val config = RealmConfiguration.Builder()
|
|
||||||
.deleteRealmIfMigrationNeeded()
|
|
||||||
.build()
|
|
||||||
Realm.setDefaultConfiguration(config)
|
|
||||||
|
|
||||||
calibrateVersions()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private fun calibrateVersions() {
|
|
||||||
// 2.0 Tolerance
|
|
||||||
if (Preferences.clockTextSize > 50f) {
|
|
||||||
Preferences.clockTextSize = 32f
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Preferences.textMainSize > 36f) {
|
|
||||||
Preferences.textMainSize = 32f
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Preferences.textSecondSize > 28f) {
|
|
||||||
Preferences.textSecondSize = 24f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,85 +3,163 @@ package com.tommasoberlose.anotherwidget.components
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.text.Editable
|
import android.view.LayoutInflater
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.GridLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.annotation.ColorInt
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuHorBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuListBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.copyToClipboard
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isClipboardColor
|
||||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
|
||||||
import com.tommasoberlose.anotherwidget.utils.expand
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.pasteFromClipboard
|
||||||
import com.tommasoberlose.anotherwidget.utils.reveal
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.*
|
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.view.*
|
import com.warkiz.widget.IndicatorSeekBar
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.view.color_loader
|
import com.warkiz.widget.OnSeekChangeListener
|
||||||
import kotlinx.android.synthetic.main.color_picker_menu_item.view.*
|
import com.warkiz.widget.SeekParams
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.lang.Exception
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
import java.util.prefs.Preferences
|
|
||||||
|
|
||||||
class BottomSheetColorPicker(
|
class BottomSheetColorPicker(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val colors: IntArray = intArrayOf(),
|
private val colors: IntArray = intArrayOf(),
|
||||||
private val selected: Int? = null,
|
private val getSelected: (() -> Int)? = null,
|
||||||
private val header: String? = null,
|
private val header: String? = null,
|
||||||
private val onColorSelected: ((selectedValue: Int) -> Unit)? = null
|
private val onColorSelected: ((selectedValue: Int) -> Unit)? = null,
|
||||||
|
private val showAlphaSelector: Boolean = false,
|
||||||
|
private val alpha: Int = 0,
|
||||||
|
private val onAlphaChangeListener: ((alpha: Int) -> Unit)? = null,
|
||||||
|
private val hideCopyPaste: Boolean = false,
|
||||||
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
private val loadingJob: Job? = null
|
private var loadingJobs: ArrayList<Job> = ArrayList()
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private var alphaDebouncing: Job? = null
|
||||||
|
|
||||||
|
private var binding: BottomSheetMenuHorBinding = BottomSheetMenuHorBinding.inflate(LayoutInflater.from(context))
|
||||||
|
private var listBinding: BottomSheetMenuListBinding = BottomSheetMenuListBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
override fun show() {
|
override fun show() {
|
||||||
val view = View.inflate(context, R.layout.bottom_sheet_menu_hor, null)
|
window?.setDimAmount(0f)
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
view.header.isVisible = header != null
|
binding.header.isVisible = header != null
|
||||||
view.header_text.text = header ?: ""
|
binding.headerText.text = header ?: ""
|
||||||
|
|
||||||
val itemViews: ArrayList<View> = ArrayList()
|
if (hideCopyPaste) {
|
||||||
|
binding.actionContainer.isVisible = false
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
} else {
|
||||||
for (@ColorInt color: Int in colors) {
|
binding.actionContainer.isVisible = true
|
||||||
val itemView = View.inflate(context, R.layout.color_picker_menu_item, null)
|
binding.actionCopy.setOnClickListener {
|
||||||
itemView.color.setCardBackgroundColor(ColorStateList.valueOf(color))
|
context.copyToClipboard(getSelected?.invoke(), alpha)
|
||||||
itemView.check.setColorFilter(ContextCompat.getColor(context,
|
|
||||||
if (color.isColorDark()) android.R.color.white else android.R.color.black
|
|
||||||
), android.graphics.PorterDuff.Mode.MULTIPLY)
|
|
||||||
itemView.check.isVisible = selected == color
|
|
||||||
itemView.color.setOnClickListener {
|
|
||||||
onColorSelected?.invoke(color)
|
|
||||||
this@BottomSheetColorPicker.dismiss()
|
|
||||||
}
|
|
||||||
itemViews.add(itemView)
|
|
||||||
}
|
}
|
||||||
|
binding.actionPaste.setOnClickListener {
|
||||||
|
context.pasteFromClipboard { color, alpha ->
|
||||||
|
binding.alphaSelector.setProgress(alpha.toIntValue().toFloat())
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
adapter.notifyItemChanged(adapter.data.indexOf(getSelected?.invoke()))
|
||||||
itemViews.forEach {
|
onColorSelected?.invoke(Color.parseColor(color))
|
||||||
view.menu.addView(it, GridLayout.LayoutParams(
|
val idx = colors.toList().indexOf(getSelected?.invoke())
|
||||||
GridLayout.spec(GridLayout.UNDEFINED, 1f),
|
adapter.notifyItemChanged(idx)
|
||||||
GridLayout.spec(GridLayout.UNDEFINED, 1f)
|
(listBinding.root.layoutManager as GridLayoutManager).scrollToPositionWithOffset(idx,0)
|
||||||
))
|
|
||||||
}
|
}
|
||||||
color_loader.isVisible = false
|
}
|
||||||
view.menu.isVisible = true
|
binding.actionPaste.isVisible = context.isClipboardColor()
|
||||||
this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
}
|
||||||
|
|
||||||
|
// Alpha
|
||||||
|
binding.alphaSelectorContainer.isVisible = showAlphaSelector
|
||||||
|
binding.alphaSelector.setProgress(alpha.toFloat())
|
||||||
|
binding.textAlpha.text = "%s: %s%%".format(context.getString(R.string.alpha), alpha)
|
||||||
|
binding.alphaSelector.onSeekChangeListener = object : OnSeekChangeListener {
|
||||||
|
override fun onSeeking(seekParams: SeekParams?) {
|
||||||
|
seekParams?.let {
|
||||||
|
binding.textAlpha.text =
|
||||||
|
"%s: %s%%".format(context.getString(R.string.alpha), it.progress)
|
||||||
|
alphaDebouncing?.cancel()
|
||||||
|
alphaDebouncing = GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
delay(150)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onAlphaChangeListener?.invoke(it.progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onStartTrackingTouch(seekBar: IndicatorSeekBar?) {
|
||||||
|
}
|
||||||
|
override fun onStopTrackingTouch(seekBar: IndicatorSeekBar?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Menu
|
// List
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
|
||||||
setContentView(view)
|
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
listBinding.root.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = GridLayoutManager(context, 6)
|
||||||
|
listBinding.root.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter
|
||||||
|
.register<Int>(R.layout.color_picker_menu_item) { item, injector ->
|
||||||
|
injector
|
||||||
|
.with<MaterialCardView>(R.id.color) {
|
||||||
|
it.setCardBackgroundColor(ColorStateList.valueOf(item))
|
||||||
|
it.strokeWidth = if ((colors.indexOf(item) == 0 && !context.isDarkTheme()) || (colors.indexOf(item) == 10 && context.isDarkTheme())) 2 else 0
|
||||||
|
}
|
||||||
|
.with<AppCompatImageView>(R.id.check) {
|
||||||
|
if (getSelected?.invoke() == item) {
|
||||||
|
it.setColorFilter(
|
||||||
|
ContextCompat.getColor(
|
||||||
|
context,
|
||||||
|
if (item.isColorDark()) android.R.color.white else android.R.color.black
|
||||||
|
),
|
||||||
|
android.graphics.PorterDuff.Mode.MULTIPLY
|
||||||
|
)
|
||||||
|
it.isVisible = true
|
||||||
|
} else {
|
||||||
|
it.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.clicked(R.id.color) {
|
||||||
|
adapter.notifyItemChanged(adapter.data.indexOf(getSelected?.invoke()))
|
||||||
|
onColorSelected?.invoke(item)
|
||||||
|
val position = adapter.data.indexOf(item)
|
||||||
|
adapter.notifyItemChanged(position)
|
||||||
|
(listBinding.root.layoutManager as GridLayoutManager).scrollToPositionWithOffset(position,0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attachTo(listBinding.root)
|
||||||
|
|
||||||
|
adapter.updateData(colors.toList())
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.isVisible = false
|
||||||
|
binding.listContainer.addView(listBinding.root)
|
||||||
|
binding.listContainer.isVisible = true
|
||||||
|
|
||||||
|
val idx = colors.toList().indexOf(getSelected?.invoke())
|
||||||
|
(listBinding.root.layoutManager as GridLayoutManager).scrollToPositionWithOffset(idx,0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
super.show()
|
super.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
loadingJob?.cancel()
|
loadingJobs.forEach { it.cancel() }
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,33 +1,32 @@
|
|||||||
package com.tommasoberlose.anotherwidget.components
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.widget.TextView
|
||||||
import androidx.annotation.MenuRes
|
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.android.material.card.MaterialCardView
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_menu.view.*
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuBinding
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_menu_item.view.*
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuItemBinding
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [BottomSheetDialogFragment] that uses a custom
|
* [BottomSheetDialogFragment] that uses a custom
|
||||||
* theme which sets a rounded background to the dialog
|
* theme which sets a rounded background to the dialog
|
||||||
* and doesn't dim the navigation bar
|
* and doesn't dim the navigation bar
|
||||||
*/
|
*/
|
||||||
open class BottomSheetMenu<T>(context: Context, private val header: String? = null, private val isMultiSelection: Boolean = false) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
open class BottomSheetMenu<T>(context: Context, private val header: String? = null, private val message: String? = null, private val isMessageWarning: Boolean = false, private val isMultiSelection: Boolean = false) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
private val items: ArrayList<MenuItem<T>> = ArrayList()
|
private val items: ArrayList<MenuItem<T>> = ArrayList()
|
||||||
private var selectedRes: ArrayList<T> = ArrayList()
|
private var selectedRes: ArrayList<T> = ArrayList()
|
||||||
private var callback: ((selectedValue: T) -> Unit)? = null
|
private var callback: ((selectedValue: T) -> Unit)? = null
|
||||||
private var multipleSelectionCallback: ((selectedValues: ArrayList<T>) -> Unit)? = null
|
private var multipleSelectionCallback: ((selectedValues: ArrayList<T>) -> Unit)? = null
|
||||||
|
|
||||||
|
private var binding = BottomSheetMenuBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
fun setSelectedValue(res: T): BottomSheetMenu<T> {
|
fun setSelectedValue(res: T): BottomSheetMenu<T> {
|
||||||
selectedRes = ArrayList(listOf(res))
|
selectedRes = ArrayList(listOf(res))
|
||||||
return this
|
return this
|
||||||
@ -38,8 +37,8 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addItem(title: String, value: T? = null): BottomSheetMenu<T> {
|
fun addItem(title: String, value: T? = null, renderCallback: ((view: TextView) -> Unit)? = null): BottomSheetMenu<T> {
|
||||||
items.add(MenuItem(title, value))
|
items.add(MenuItem(title, value, renderCallback))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,29 +53,31 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun show() {
|
override fun show() {
|
||||||
val view = View.inflate(context, R.layout.bottom_sheet_menu, null)
|
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
view.header.isVisible = header != null
|
binding.header.isVisible = header != null
|
||||||
view.header_text.text = header ?: ""
|
binding.headerText.text = header ?: ""
|
||||||
|
|
||||||
|
binding.warningText.isVisible = message != null
|
||||||
|
binding.warningText.text = message ?: ""
|
||||||
|
binding.warningText.setTextColor(ContextCompat.getColor(context, if (isMessageWarning) R.color.warningColorText else R.color.colorSecondaryText))
|
||||||
|
|
||||||
// Menu
|
// Menu
|
||||||
for (item in items) {
|
for (item in items) {
|
||||||
|
val itemBinding = BottomSheetMenuItemBinding.inflate(LayoutInflater.from(context))
|
||||||
if (item.value != null) {
|
if (item.value != null) {
|
||||||
val itemView = View.inflate(context, R.layout.bottom_sheet_menu_item, null)
|
itemBinding.label.text = item.title
|
||||||
itemView.label.text = item.title
|
|
||||||
if (isMultiSelection) {
|
if (isMultiSelection) {
|
||||||
itemView.icon_check.isVisible = selectedRes.contains(item.value)
|
itemBinding.iconCheck.isVisible = selectedRes.contains(item.value)
|
||||||
itemView.label.setTextColor(
|
itemBinding.label.setTextColor(
|
||||||
if (selectedRes.contains(item.value)) ContextCompat.getColor(
|
if (selectedRes.contains(item.value)) ContextCompat.getColor(
|
||||||
context,
|
context,
|
||||||
R.color.colorPrimaryText
|
R.color.colorPrimaryText
|
||||||
) else ContextCompat.getColor(context, R.color.colorSecondaryText)
|
) else ContextCompat.getColor(context, R.color.colorSecondaryText)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
itemView.isSelected = selectedRes.contains(item.value)
|
itemBinding.root.isSelected = selectedRes.contains(item.value)
|
||||||
}
|
}
|
||||||
itemView.setOnClickListener {
|
itemBinding.root.setOnClickListener {
|
||||||
if (!isMultiSelection) {
|
if (!isMultiSelection) {
|
||||||
callback?.invoke(item.value)
|
callback?.invoke(item.value)
|
||||||
this.dismiss()
|
this.dismiss()
|
||||||
@ -88,26 +89,32 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
|
|||||||
}
|
}
|
||||||
|
|
||||||
multipleSelectionCallback?.invoke(selectedRes)
|
multipleSelectionCallback?.invoke(selectedRes)
|
||||||
itemView.icon_check.isVisible = selectedRes.contains(item.value)
|
itemBinding.iconCheck.isVisible = selectedRes.contains(item.value)
|
||||||
itemView.label.setTextColor(
|
itemBinding.label.setTextColor(
|
||||||
if (selectedRes.contains(item.value)) ContextCompat.getColor(
|
if (selectedRes.contains(item.value)) ContextCompat.getColor(
|
||||||
context,
|
context,
|
||||||
R.color.colorPrimaryText
|
R.color.colorPrimaryText
|
||||||
) else ContextCompat.getColor(context, R.color.colorSecondaryText)
|
) else ContextCompat.getColor(context, R.color.colorSecondaryText)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.renderCallback?.invoke(itemBinding.label)
|
||||||
}
|
}
|
||||||
view.menu.addView(itemView)
|
|
||||||
|
binding.menu.addView(itemBinding.root)
|
||||||
} else {
|
} else {
|
||||||
val itemView = View.inflate(context, R.layout.bottom_sheet_menu_divider, null)
|
itemBinding.label.text = item.title
|
||||||
itemView.label.text = item.title
|
binding.menu.addView(itemBinding.root)
|
||||||
view.menu.addView(itemView)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setContentView(view)
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
super.show()
|
super.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
class MenuItem<T>(val title: String, val value: T? = null)
|
class MenuItem<T>(val title: String, val value: T? = null, val renderCallback: ((view: TextView) -> Unit)? = null)
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuHorBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuListBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.copyToClipboard
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isClipboardColor
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.pasteFromClipboard
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||||
|
import com.warkiz.widget.IndicatorSeekBar
|
||||||
|
import com.warkiz.widget.OnSeekChangeListener
|
||||||
|
import com.warkiz.widget.SeekParams
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
|
||||||
|
class BottomSheetPicker<T>(
|
||||||
|
context: Context,
|
||||||
|
private val items: List<MenuItem<T>> = arrayListOf(),
|
||||||
|
private val getSelected: (() -> T)? = null,
|
||||||
|
private val header: String? = null,
|
||||||
|
private val onItemSelected: ((selectedValue: T?) -> Unit)? = null,
|
||||||
|
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
|
private var loadingJobs: ArrayList<Job> = ArrayList()
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
|
||||||
|
private var binding: BottomSheetMenuHorBinding = BottomSheetMenuHorBinding.inflate(
|
||||||
|
LayoutInflater.from(context))
|
||||||
|
private var listBinding: BottomSheetMenuListBinding = BottomSheetMenuListBinding.inflate(
|
||||||
|
LayoutInflater.from(context))
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
window?.setDimAmount(0f)
|
||||||
|
|
||||||
|
// Header
|
||||||
|
binding.header.isVisible = header != null
|
||||||
|
binding.headerText.text = header ?: ""
|
||||||
|
|
||||||
|
// Alpha
|
||||||
|
binding.alphaSelectorContainer.isVisible = false
|
||||||
|
binding.actionContainer.isVisible = false
|
||||||
|
|
||||||
|
// List
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
|
||||||
|
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
listBinding.root.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(context)
|
||||||
|
listBinding.root.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter
|
||||||
|
.register<Int>(R.layout.bottom_sheet_menu_item) { position, injector ->
|
||||||
|
val item = items[position]
|
||||||
|
val isSelected = item.value == getSelected?.invoke()
|
||||||
|
injector
|
||||||
|
.text(R.id.label, item.title)
|
||||||
|
.textColor(R.id.label, ContextCompat.getColor(context, if (isSelected) R.color.colorAccent else R.color.colorSecondaryText))
|
||||||
|
.selected(R.id.item, isSelected)
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
val oldIdx = items.toList().indexOfFirst { it.value == getSelected?.invoke() }
|
||||||
|
onItemSelected?.invoke(item.value)
|
||||||
|
adapter.notifyItemChanged(position)
|
||||||
|
adapter.notifyItemChanged(oldIdx)
|
||||||
|
(listBinding.root.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position,0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attachTo(listBinding.root)
|
||||||
|
|
||||||
|
adapter.updateData((items.indices).toList())
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.isVisible = false
|
||||||
|
binding.listContainer.addView(listBinding.root)
|
||||||
|
binding.listContainer.isVisible = true
|
||||||
|
|
||||||
|
val idx = items.toList().indexOfFirst { it.value == getSelected?.invoke() }
|
||||||
|
(listBinding.root.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(idx,0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
super.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
loadingJobs.forEach { it.cancel() }
|
||||||
|
super.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
class MenuItem<T>(val title: String, val value: T? = null)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.WeatherProviderSettingsLayoutBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||||
|
|
||||||
|
class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
|
private var binding: WeatherProviderSettingsLayoutBinding = WeatherProviderSettingsLayoutBinding.inflate(android.view.LayoutInflater.from(context))
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.apiKeyContainer.isVisible = WeatherHelper.isKeyRequired()
|
||||||
|
binding.actionSaveKey.isVisible = WeatherHelper.isKeyRequired()
|
||||||
|
|
||||||
|
WeatherHelper.getProviderInfoTitle(context).let { title ->
|
||||||
|
binding.infoTitle.text = title
|
||||||
|
binding.infoTitle.isVisible = title != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherHelper.getProviderInfoSubtitle(context).let { subtitle ->
|
||||||
|
binding.infoSubtitle.text = subtitle
|
||||||
|
binding.infoSubtitle.isVisible = subtitle != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.infoProvider.text = WeatherHelper.getProviderName(context)
|
||||||
|
|
||||||
|
binding.apiKey.editText?.setText(when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi
|
||||||
|
Constants.WeatherProvider.HERE -> Preferences.weatherProviderApiHere
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> Preferences.weatherProviderApiAccuweather
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV,
|
||||||
|
Constants.WeatherProvider.YR,
|
||||||
|
null -> ""
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.actionOpenProvider.setOnClickListener {
|
||||||
|
context.openURI(WeatherHelper.getProviderLink())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.actionSaveKey.setOnClickListener {
|
||||||
|
val key = binding.apiKey.editText?.text.toString()
|
||||||
|
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen = key
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit = key
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi = key
|
||||||
|
Constants.WeatherProvider.HERE -> Preferences.weatherProviderApiHere = key
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> Preferences.weatherProviderApiAccuweather = key
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
callback.invoke()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.CustomNotesDialogLayoutBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
|
||||||
|
class CustomNotesDialog(context: Context, callback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
|
private var binding: CustomNotesDialogLayoutBinding = CustomNotesDialogLayoutBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.notes.setText(Preferences.customNotes)
|
||||||
|
|
||||||
|
binding.actionPositive.setOnClickListener {
|
||||||
|
Preferences.customNotes = binding.notes.text.toString()
|
||||||
|
this.dismiss()
|
||||||
|
callback?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.notes.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ScrollView
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
|
|
||||||
|
|
||||||
|
class FixedFocusScrollView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyle: Int = 0
|
||||||
|
) : NestedScrollView(context, attrs, defStyle) {
|
||||||
|
|
||||||
|
var isScrollable = true
|
||||||
|
|
||||||
|
override fun scrollTo(x: Int, y: Int) {
|
||||||
|
if (isScrollable || !isLaidOut) {
|
||||||
|
super.scrollTo(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,292 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.GlanceProviderSettingsLayoutBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.GreetingsHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.activities.tabs.AppNotificationsFilterActivity
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.activities.tabs.MediaInfoFormatActivity
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.activities.tabs.MusicPlayersFilterActivity
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
|
class GlanceSettingsDialog(val context: Activity, val provider: Constants.GlanceProviderId, private val statusCallback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
|
private var binding: GlanceProviderSettingsLayoutBinding = GlanceProviderSettingsLayoutBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
|
||||||
|
/* TITLE */
|
||||||
|
binding.title.text = when (provider) {
|
||||||
|
Constants.GlanceProviderId.PLAYING_SONG -> context.getString(R.string.settings_show_music_title)
|
||||||
|
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> context.getString(R.string.settings_show_next_alarm_title)
|
||||||
|
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> context.getString(R.string.settings_low_battery_level_title)
|
||||||
|
Constants.GlanceProviderId.CUSTOM_INFO -> context.getString(R.string.settings_custom_notes_title)
|
||||||
|
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> context.getString(R.string.settings_daily_steps_title)
|
||||||
|
Constants.GlanceProviderId.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_title)
|
||||||
|
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_title)
|
||||||
|
Constants.GlanceProviderId.EVENTS -> context.getString(R.string.settings_show_events_as_glance_provider_title)
|
||||||
|
Constants.GlanceProviderId.WEATHER -> context.getString(R.string.settings_show_weather_as_glance_provider_title)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SUBTITLE*/
|
||||||
|
binding.subtitle.text = when (provider) {
|
||||||
|
Constants.GlanceProviderId.PLAYING_SONG -> context.getString(R.string.settings_show_music_subtitle)
|
||||||
|
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> context.getString(R.string.settings_show_next_alarm_subtitle)
|
||||||
|
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> context.getString(R.string.settings_low_battery_level_subtitle)
|
||||||
|
Constants.GlanceProviderId.CUSTOM_INFO -> ""
|
||||||
|
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> context.getString(R.string.settings_daily_steps_subtitle)
|
||||||
|
Constants.GlanceProviderId.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_subtitle)
|
||||||
|
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_subtitle)
|
||||||
|
Constants.GlanceProviderId.EVENTS -> context.getString(R.string.settings_show_events_as_glance_provider_subtitle)
|
||||||
|
Constants.GlanceProviderId.WEATHER -> context.getString(R.string.settings_show_weather_as_glance_provider_subtitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SONG */
|
||||||
|
binding.actionFilterMusicPlayers.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG
|
||||||
|
binding.actionChangeMediaInfoFormat.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG
|
||||||
|
if (provider == Constants.GlanceProviderId.PLAYING_SONG) {
|
||||||
|
binding.actionFilterMusicPlayers.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
context.startActivityForResult(Intent(context, MusicPlayersFilterActivity::class.java), 0)
|
||||||
|
}
|
||||||
|
binding.actionChangeMediaInfoFormat.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
context.startActivityForResult(Intent(context, MediaInfoFormatActivity::class.java), 0)
|
||||||
|
}
|
||||||
|
checkNotificationPermission()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ALARM */
|
||||||
|
binding.alarmSetByContainer.isVisible = provider == Constants.GlanceProviderId.NEXT_CLOCK_ALARM
|
||||||
|
if (provider == Constants.GlanceProviderId.NEXT_CLOCK_ALARM) {
|
||||||
|
binding.header.text = context.getString(R.string.information_header)
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
checkNextAlarm()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BATTERY INFO */
|
||||||
|
if (provider == Constants.GlanceProviderId.BATTERY_LEVEL_LOW) {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
binding.header.isVisible = false
|
||||||
|
binding.divider.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTIFICATIONS */
|
||||||
|
binding.actionFilterNotificationsApp.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
|
||||||
|
binding.actionChangeNotificationTimer.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
|
||||||
|
if (provider == Constants.GlanceProviderId.NOTIFICATIONS) {
|
||||||
|
checkLastNotificationsPermission()
|
||||||
|
val stringArray = context.resources.getStringArray(R.array.glance_notifications_timeout)
|
||||||
|
binding.actionFilterNotificationsApp.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
context.startActivityForResult(Intent(context, AppNotificationsFilterActivity::class.java), 0)
|
||||||
|
}
|
||||||
|
binding.notificationTimerLabel.text = stringArray[Preferences.hideNotificationAfter]
|
||||||
|
binding.actionChangeNotificationTimer.setOnClickListener {
|
||||||
|
val dialog = BottomSheetMenu<Int>(context, header = context.getString(R.string.glance_notification_hide_timeout_title)).setSelectedValue(Preferences.hideNotificationAfter)
|
||||||
|
Constants.GlanceNotificationTimer.values().forEachIndexed { index, timeout ->
|
||||||
|
dialog.addItem(stringArray[index], timeout.rawValue)
|
||||||
|
}
|
||||||
|
dialog.addOnSelectItemListener { value ->
|
||||||
|
Preferences.hideNotificationAfter = value
|
||||||
|
this.show()
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GREETINGS */
|
||||||
|
if (provider == Constants.GlanceProviderId.GREETINGS) {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
binding.header.isVisible = false
|
||||||
|
binding.divider.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EVENTS */
|
||||||
|
if (provider == Constants.GlanceProviderId.EVENTS) {
|
||||||
|
binding.header.isVisible = false
|
||||||
|
binding.divider.isVisible = false
|
||||||
|
checkCalendarConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WEATHER */
|
||||||
|
if (provider == Constants.GlanceProviderId.WEATHER) {
|
||||||
|
binding.header.isVisible = false
|
||||||
|
binding.divider.isVisible = false
|
||||||
|
checkWeatherConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TOGGLE */
|
||||||
|
binding.providerSwitch.setCheckedImmediatelyNoEvent(when (provider) {
|
||||||
|
Constants.GlanceProviderId.PLAYING_SONG -> Preferences.showMusic
|
||||||
|
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> Preferences.showNextAlarm
|
||||||
|
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> Preferences.showBatteryCharging
|
||||||
|
Constants.GlanceProviderId.CUSTOM_INFO -> true
|
||||||
|
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> Preferences.showDailySteps
|
||||||
|
Constants.GlanceProviderId.NOTIFICATIONS -> Preferences.showNotifications
|
||||||
|
Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings
|
||||||
|
Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider
|
||||||
|
Constants.GlanceProviderId.WEATHER -> Preferences.showWeatherAsGlanceProvider
|
||||||
|
})
|
||||||
|
|
||||||
|
var job: Job? = null
|
||||||
|
|
||||||
|
binding.providerSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
job?.cancel()
|
||||||
|
job = GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
delay(300)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
when (provider) {
|
||||||
|
Constants.GlanceProviderId.PLAYING_SONG -> {
|
||||||
|
Preferences.showMusic = isChecked
|
||||||
|
checkNotificationPermission()
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
|
||||||
|
Preferences.showNextAlarm = isChecked
|
||||||
|
checkNextAlarm()
|
||||||
|
if (!isChecked)
|
||||||
|
AlarmHelper.clearTimeout(context)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
|
||||||
|
Preferences.showBatteryCharging = isChecked
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.NOTIFICATIONS -> {
|
||||||
|
Preferences.showNotifications = isChecked
|
||||||
|
checkLastNotificationsPermission()
|
||||||
|
if (!isChecked)
|
||||||
|
ActiveNotificationsHelper.clearLastNotification(context)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.GREETINGS -> {
|
||||||
|
Preferences.showGreetings = isChecked
|
||||||
|
GreetingsHelper.toggleGreetings(context)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.EVENTS -> {
|
||||||
|
Preferences.showEventsAsGlanceProvider = isChecked
|
||||||
|
if (isChecked) {
|
||||||
|
com.tommasoberlose.anotherwidget.db.EventRepository(context).run {
|
||||||
|
resetNextEventData()
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.WEATHER -> {
|
||||||
|
Preferences.showWeatherAsGlanceProvider = isChecked
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusCallback?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
super.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkNextAlarm() {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val alarm = nextAlarmClock
|
||||||
|
if (alarm != null && alarm.showIntent != null) {
|
||||||
|
val pm = context.packageManager as PackageManager
|
||||||
|
val appNameOrPackage = try {
|
||||||
|
pm.getApplicationLabel(pm.getApplicationInfo(alarm.showIntent?.creatorPackage ?: "", 0))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
alarm.showIntent?.creatorPackage ?: ""
|
||||||
|
}
|
||||||
|
binding.alarmSetByTitle.text = context.getString(R.string.settings_show_next_alarm_app_title).format(appNameOrPackage)
|
||||||
|
binding.alarmSetBySubtitle.text = if (AlarmHelper.isAlarmProbablyWrong(context)) context.getString(R.string.settings_show_next_alarm_app_subtitle_wrong) else context.getString(R.string.settings_show_next_alarm_app_subtitle_correct)
|
||||||
|
binding.alarmSetByContainer.isVisible = true
|
||||||
|
} else {
|
||||||
|
binding.alarmSetByContainer.isVisible = false
|
||||||
|
binding.header.isVisible = false
|
||||||
|
binding.divider.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusCallback?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCalendarConfig() {
|
||||||
|
if (!Preferences.showEvents || !context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
|
||||||
|
binding.warningContainer.isVisible = true
|
||||||
|
binding.warningTitle.text = context.getString(R.string.settings_show_events_as_glance_provider_error)
|
||||||
|
binding.warningContainer.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
EventBus.getDefault().post(MainFragment.ChangeTabEvent(1))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkWeatherConfig() {
|
||||||
|
if (!Preferences.showWeather || (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") || Preferences.weatherProviderLocationError != "") {
|
||||||
|
binding.warningContainer.isVisible = true
|
||||||
|
binding.warningTitle.text = context.getString(R.string.settings_show_weather_as_glance_provider_error)
|
||||||
|
binding.warningContainer.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
EventBus.getDefault().post(MainFragment.ChangeTabEvent(1))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkNotificationPermission() {
|
||||||
|
when {
|
||||||
|
ActiveNotificationsHelper.checkNotificationAccess(context) -> {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||||
|
}
|
||||||
|
Preferences.showMusic -> {
|
||||||
|
binding.warningContainer.isVisible = true
|
||||||
|
binding.warningTitle.text = context.getString(R.string.settings_request_notification_access)
|
||||||
|
binding.warningContainer.setOnClickListener {
|
||||||
|
context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusCallback?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkLastNotificationsPermission() {
|
||||||
|
when {
|
||||||
|
ActiveNotificationsHelper.checkNotificationAccess(context) -> {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
}
|
||||||
|
Preferences.showNotifications -> {
|
||||||
|
binding.warningContainer.isVisible = true
|
||||||
|
binding.warningTitle.text = context.getString(R.string.settings_request_last_notification_access)
|
||||||
|
binding.warningContainer.setOnClickListener {
|
||||||
|
context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
binding.warningContainer.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusCallback?.invoke()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.IconPackMenuItemBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||||
|
|
||||||
|
class IconPackSelector(context: Context, private val header: String? = null) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
|
private var binding = BottomSheetMenuBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
// Header
|
||||||
|
binding.header.isVisible = header != null
|
||||||
|
binding.headerText.text = header ?: ""
|
||||||
|
|
||||||
|
binding.warningText.isVisible = false
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
for (item in Constants.WeatherIconPack.values()) {
|
||||||
|
val itemBinding = IconPackMenuItemBinding.inflate(LayoutInflater.from(context))
|
||||||
|
itemBinding.label.text = context.getString(R.string.settings_weather_icon_pack_default).format(item.rawValue + 1)
|
||||||
|
itemBinding.root.isSelected = item.rawValue == Preferences.weatherIconPack
|
||||||
|
|
||||||
|
itemBinding.icon1.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01d", item.rawValue)))
|
||||||
|
itemBinding.icon2.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01n", item.rawValue)))
|
||||||
|
itemBinding.icon3.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "10d", item.rawValue)))
|
||||||
|
itemBinding.icon4.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "09n", item.rawValue)))
|
||||||
|
|
||||||
|
listOf<ImageView>(itemBinding.icon1, itemBinding.icon2, itemBinding.icon3, itemBinding.icon4).forEach {
|
||||||
|
if (item == Constants.WeatherIconPack.MINIMAL) {
|
||||||
|
it.setColorFilter(ContextCompat.getColor(context, R.color.colorPrimaryText))
|
||||||
|
} else {
|
||||||
|
it.setColorFilter(ContextCompat.getColor(context, android.R.color.transparent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemBinding.root.setOnClickListener {
|
||||||
|
Preferences.weatherIconPack = item.rawValue
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
binding.menu.addView(itemBinding.root)
|
||||||
|
}
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
super.show()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.BottomSheetDialogBinding
|
||||||
|
|
||||||
|
typealias DialogCallback = () -> Unit
|
||||||
|
|
||||||
|
class MaterialBottomSheetDialog(
|
||||||
|
context: Context,
|
||||||
|
private val title: String? = null,
|
||||||
|
private val message: String? = null
|
||||||
|
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||||
|
|
||||||
|
private var positiveButtonLabel: String? = null
|
||||||
|
private var negativeButtonLabel: String? = null
|
||||||
|
private var positiveCallback: DialogCallback? = null
|
||||||
|
private var negativeCallback: DialogCallback? = null
|
||||||
|
|
||||||
|
private var binding = BottomSheetDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
fun setPositiveButton(label: String? = context.getString(android.R.string.ok), callback: DialogCallback? = null): MaterialBottomSheetDialog {
|
||||||
|
positiveButtonLabel = label
|
||||||
|
positiveCallback = callback
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNegativeButton(label: String? = context.getString(android.R.string.cancel), callback: DialogCallback? = null): MaterialBottomSheetDialog {
|
||||||
|
negativeButtonLabel = label
|
||||||
|
negativeCallback = callback
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
// Header
|
||||||
|
binding.title.isVisible = title != null
|
||||||
|
binding.title.text = title ?: ""
|
||||||
|
|
||||||
|
binding.message.isVisible = message != null
|
||||||
|
binding.message.text = message ?: ""
|
||||||
|
|
||||||
|
binding.actionPositive.isVisible = positiveButtonLabel != null
|
||||||
|
binding.actionPositive.text = positiveButtonLabel ?: ""
|
||||||
|
binding.actionPositive.setOnClickListener {
|
||||||
|
positiveCallback?.invoke()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.actionNegative.isVisible = negativeButtonLabel != null
|
||||||
|
binding.actionNegative.text = negativeButtonLabel ?: ""
|
||||||
|
binding.actionNegative.setOnClickListener {
|
||||||
|
negativeCallback?.invoke()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
behavior.run {
|
||||||
|
skipCollapsed = true
|
||||||
|
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
super.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
|
||||||
|
class MenuItem (
|
||||||
|
val icon: Int,
|
||||||
|
val getIcon: (() -> Int)? = null,
|
||||||
|
val title: String,
|
||||||
|
val label: String = "",
|
||||||
|
val getLabel: (() -> String)? = null,
|
||||||
|
val isEnabled: (() -> Boolean) = fun (): Boolean { return true },
|
||||||
|
val onClick: View.OnClickListener? = null,
|
||||||
|
val onLongClick: View.OnLongClickListener? = null,
|
||||||
|
val showToggle: Boolean = false,
|
||||||
|
val toggleValue: (() -> Boolean) = fun (): Boolean { return false },
|
||||||
|
val onToggle: CompoundButton.OnCheckedChangeListener? = null,
|
||||||
|
val showPermission: (() -> Boolean) = fun (): Boolean { return false },
|
||||||
|
val onPermissionClickListener: View.OnClickListener? = null,
|
||||||
|
val render: ((view: View) -> Unit)? = null
|
||||||
|
)
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.components
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
class OnSingleClickListener : View.OnClickListener {
|
||||||
|
|
||||||
|
private val onClickListener: View.OnClickListener
|
||||||
|
|
||||||
|
constructor(listener: View.OnClickListener) {
|
||||||
|
onClickListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(listener: (View) -> Unit) {
|
||||||
|
onClickListener = View.OnClickListener { listener.invoke(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
val currentTimeMillis = System.currentTimeMillis()
|
||||||
|
|
||||||
|
if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
|
||||||
|
previousClickTimeMillis = currentTimeMillis
|
||||||
|
onClickListener.onClick(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val DELAY_MILLIS = 200L
|
||||||
|
private var previousClickTimeMillis = 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,84 +1,191 @@
|
|||||||
package com.tommasoberlose.anotherwidget.db
|
package com.tommasoberlose.anotherwidget.db
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
import com.chibatching.kotpref.bulk
|
import com.chibatching.kotpref.bulk
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
|
||||||
import com.tommasoberlose.anotherwidget.models.Event
|
import com.tommasoberlose.anotherwidget.models.Event
|
||||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
import io.realm.Realm
|
import java.util.*
|
||||||
import io.realm.RealmResults
|
import kotlin.Comparator
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class EventRepository(val context: Context) {
|
class EventRepository(val context: Context) {
|
||||||
private val realm by lazy { Realm.getDefaultInstance() }
|
private val db by lazy { EventDatabase.getDatabase(context) }
|
||||||
|
|
||||||
fun saveEvents(eventList: ArrayList<Event>) {
|
fun saveEvents(eventList: List<Event>) {
|
||||||
realm.executeTransactionAsync { realm ->
|
db.runInTransaction{
|
||||||
realm.where(Event::class.java).findAll().deleteAllFromRealm()
|
db.dao().run {
|
||||||
realm.copyToRealm(eventList)
|
deleteAll()
|
||||||
|
insert(eventList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetNextEventData() {
|
fun clearEvents() {
|
||||||
realm.executeTransactionAsync {
|
db.dao().deleteAll()
|
||||||
it.where(Event::class.java).findAll().deleteAllFromRealm()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
fun resetNextEventData() {
|
||||||
Preferences.bulk {
|
Preferences.bulk {
|
||||||
remove(Preferences::nextEventId)
|
remove(Preferences::nextEventId)
|
||||||
remove(Preferences::nextEventName)
|
|
||||||
remove(Preferences::nextEventStartDate)
|
|
||||||
remove(Preferences::nextEventAllDay)
|
|
||||||
remove(Preferences::nextEventLocation)
|
|
||||||
remove(Preferences::nextEventEndDate)
|
|
||||||
remove(Preferences::nextEventCalendarId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWidget.updateWidget(context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveNextEventData(event: Event) {
|
fun saveNextEventData(event: Event) {
|
||||||
Preferences.nextEventId = event.id
|
Preferences.nextEventId = event.id
|
||||||
MainWidget.updateWidget(context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNextEvent(): Event? = realm.where(Event::class.java).equalTo("id", Preferences.nextEventId).findFirst() ?: realm.where(Event::class.java).findFirst()
|
fun getNextEvent(): Event? {
|
||||||
|
val nextEvent = getEventById(Preferences.nextEventId)
|
||||||
|
val now = Calendar.getInstance().timeInMillis
|
||||||
|
val limit = Calendar.getInstance().apply {
|
||||||
|
timeInMillis = now
|
||||||
|
when (Preferences.showUntil) {
|
||||||
|
0 -> add(Calendar.HOUR, 3)
|
||||||
|
1 -> add(Calendar.HOUR, 6)
|
||||||
|
2 -> add(Calendar.HOUR, 12)
|
||||||
|
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||||
|
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||||
|
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||||
|
6 -> add(Calendar.MINUTE, 30)
|
||||||
|
7 -> add(Calendar.HOUR, 1)
|
||||||
|
else -> add(Calendar.HOUR, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) {
|
||||||
|
nextEvent
|
||||||
|
} else {
|
||||||
|
val events = getEvents()
|
||||||
|
if (events.isNotEmpty()) {
|
||||||
|
val newNextEvent = events.first()
|
||||||
|
saveNextEventData(newNextEvent)
|
||||||
|
newNextEvent
|
||||||
|
} else {
|
||||||
|
resetNextEventData()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEventById(id: Long): Event? {
|
||||||
|
return db.dao().findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
fun goToNextEvent() {
|
fun goToNextEvent() {
|
||||||
val eventList = realm.where(Event::class.java).findAll()
|
val eventList = getEvents()
|
||||||
|
|
||||||
if (eventList.isNotEmpty()) {
|
if (eventList.isNotEmpty()) {
|
||||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||||
if (index > -1 && index < eventList.size - 1) {
|
if (index > -1 && index < eventList.size - 1) {
|
||||||
Preferences.nextEventId = eventList[index + 1]!!.id
|
saveNextEventData(eventList[index + 1])
|
||||||
} else {
|
} else {
|
||||||
Preferences.nextEventId = eventList.first()!!.id
|
saveNextEventData(eventList.first())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetNextEventData()
|
resetNextEventData()
|
||||||
}
|
}
|
||||||
UpdatesReceiver.setUpdates(context)
|
|
||||||
MainWidget.updateWidget(context)
|
MainWidget.updateWidget(context)
|
||||||
|
org.greenrobot.eventbus.EventBus.getDefault().post(
|
||||||
|
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun goToPreviousEvent() {
|
fun goToPreviousEvent() {
|
||||||
val eventList = realm.where(Event::class.java).findAll()
|
val eventList = getEvents()
|
||||||
|
|
||||||
if (eventList.isNotEmpty()) {
|
if (eventList.isNotEmpty()) {
|
||||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
Preferences.nextEventId = eventList[index - 1]!!.id
|
saveNextEventData(eventList[index - 1])
|
||||||
} else {
|
} else {
|
||||||
Preferences.nextEventId = eventList.last()!!.id
|
saveNextEventData(eventList.last())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetNextEventData()
|
resetNextEventData()
|
||||||
}
|
}
|
||||||
UpdatesReceiver.setUpdates(context)
|
|
||||||
MainWidget.updateWidget(context)
|
MainWidget.updateWidget(context)
|
||||||
|
org.greenrobot.eventbus.EventBus.getDefault().post(
|
||||||
|
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEvents(): RealmResults<Event> = realm.where(Event::class.java).findAll()
|
fun getFutureEvents(): List<Event> {
|
||||||
|
return db.dao().find(Calendar.getInstance().timeInMillis).sortEvents()
|
||||||
|
}
|
||||||
|
|
||||||
fun getEventsCount(): Int = realm.where(Event::class.java).findAll().size
|
private fun getEvents(): List<Event> {
|
||||||
}
|
val now = Calendar.getInstance().timeInMillis
|
||||||
|
val limit = Calendar.getInstance().apply {
|
||||||
|
timeInMillis = now
|
||||||
|
when (Preferences.showUntil) {
|
||||||
|
0 -> add(Calendar.HOUR, 3)
|
||||||
|
1 -> add(Calendar.HOUR, 6)
|
||||||
|
2 -> add(Calendar.HOUR, 12)
|
||||||
|
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||||
|
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||||
|
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||||
|
6 -> add(Calendar.MINUTE, 30)
|
||||||
|
7 -> add(Calendar.HOUR, 1)
|
||||||
|
else -> add(Calendar.HOUR, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return db.dao().find(now, limit.timeInMillis).sortEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEventsCount(): Int = getEvents().size
|
||||||
|
|
||||||
|
fun close() {
|
||||||
|
// db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface EventDao {
|
||||||
|
@Query("SELECT * FROM events WHERE id = :id LIMIT 1")
|
||||||
|
fun findById(id: Long): Event?
|
||||||
|
|
||||||
|
@Query("SELECT * FROM events WHERE end_date > :from")
|
||||||
|
fun find(from: Long): List<Event>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM events WHERE end_date > :from AND start_date <= :to")
|
||||||
|
fun find(from: Long, to: Long): List<Event>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(events: List<Event>)
|
||||||
|
|
||||||
|
@Query("DELETE FROM events")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Database(entities = arrayOf(Event::class), version = 1, exportSchema = false)
|
||||||
|
abstract class EventDatabase : RoomDatabase() {
|
||||||
|
abstract fun dao(): EventDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var INSTANCE: EventDatabase? = null
|
||||||
|
|
||||||
|
fun getDatabase(context: Context): EventDatabase {
|
||||||
|
// if the INSTANCE is not null, then return it,
|
||||||
|
// if it is, then create the database
|
||||||
|
return INSTANCE ?: synchronized(this) {
|
||||||
|
val instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
EventDatabase::class.java,
|
||||||
|
"events"
|
||||||
|
).allowMainThreadQueries().build()
|
||||||
|
INSTANCE = instance
|
||||||
|
// return instance
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,12 +2,16 @@ package com.tommasoberlose.anotherwidget.global
|
|||||||
|
|
||||||
object Actions {
|
object Actions {
|
||||||
const val ACTION_EXTRA_OPEN_WEATHER_PROVIDER = "ACTION_EXTRA_OPEN_WEATHER_PROVIDER"
|
const val ACTION_EXTRA_OPEN_WEATHER_PROVIDER = "ACTION_EXTRA_OPEN_WEATHER_PROVIDER"
|
||||||
const val ACTION_EXTRA_DISABLE_GPS_NOTIFICATION = "ACTION_EXTRA_DISABLE_GPS_NOTIFICATION"
|
|
||||||
|
|
||||||
const val ACTION_TIME_UPDATE = "com.tommasoberlose.anotherwidget.action.ACTION_TIME_UPDATE"
|
const val ACTION_TIME_UPDATE = "com.tommasoberlose.anotherwidget.action.TIME_UPDATE"
|
||||||
const val ACTION_CALENDAR_UPDATE = "com.tommasoberlose.anotherwidget.action.ACTION_CALENDAR_UPDATE"
|
const val ACTION_ALARM_UPDATE = "com.tommasoberlose.anotherwidget.action.ALARM_UPDATE"
|
||||||
const val ACTION_WEATHER_UPDATE = "com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE"
|
const val ACTION_CALENDAR_UPDATE = "com.tommasoberlose.anotherwidget.action.CALENDAR_UPDATE"
|
||||||
const val ACTION_OPEN_WEATHER_INTENT = "com.tommasoberlose.anotherwidget.action.ACTION_OPEN_WEATHER_INTENT"
|
const val ACTION_WEATHER_UPDATE = "com.tommasoberlose.anotherwidget.action.WEATHER_UPDATE"
|
||||||
|
const val ACTION_OPEN_WEATHER_INTENT = "com.tommasoberlose.anotherwidget.action.OPEN_WEATHER_INTENT"
|
||||||
const val ACTION_GO_TO_NEXT_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_NEXT_EVENT"
|
const val ACTION_GO_TO_NEXT_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_NEXT_EVENT"
|
||||||
const val ACTION_GO_TO_PREVIOUS_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_PREVIOUS_EVENT"
|
const val ACTION_GO_TO_PREVIOUS_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_PREVIOUS_EVENT"
|
||||||
|
const val ACTION_CLEAR_NOTIFICATION = "com.tommasoberlose.anotherwidget.action.CLEAR_NOTIFICATION"
|
||||||
|
const val ACTION_UPDATE_GREETINGS = "com.tommasoberlose.anotherwidget.action.UPDATE_GREETINGS"
|
||||||
|
|
||||||
|
const val ACTION_REFRESH = "com.tommasoberlose.anotherwidget.action.REFRESH"
|
||||||
}
|
}
|
@ -1,17 +1,97 @@
|
|||||||
package com.tommasoberlose.anotherwidget.global
|
package com.tommasoberlose.anotherwidget.global
|
||||||
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
const val RESULT_CODE_CUSTOM_LOCATION = 45
|
const val RESULT_CODE_CUSTOM_LOCATION = 45
|
||||||
const val RESULT_APP_NAME = "RESULT_APP_NAME"
|
const val RESULT_APP_NAME = "RESULT_APP_NAME"
|
||||||
const val RESULT_APP_PACKAGE = "RESULT_APP_PACKAGE"
|
const val RESULT_APP_PACKAGE = "RESULT_APP_PACKAGE"
|
||||||
|
|
||||||
const val CUSTOM_FONT_PRODUCT_SANS = 1
|
const val CUSTOM_FONT_GOOGLE_SANS = 1
|
||||||
enum class ClockBottomMargin(val value: Int) {
|
const val CUSTOM_FONT_DOWNLOADED = 2
|
||||||
|
const val CUSTOM_FONT_DOWNLOAD_NEW = 3
|
||||||
|
|
||||||
|
enum class ClockBottomMargin(val rawValue: Int) {
|
||||||
NONE(0),
|
NONE(0),
|
||||||
SMALL(1),
|
SMALL(1),
|
||||||
MEDIUM(2),
|
MEDIUM(2),
|
||||||
LARGE(3)
|
LARGE(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SecondRowTopMargin(val rawValue: Int) {
|
||||||
|
NONE(0),
|
||||||
|
SMALL(1),
|
||||||
|
MEDIUM(2),
|
||||||
|
LARGE(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Dimension(val rawValue: Float) {
|
||||||
|
NONE(0f),
|
||||||
|
SMALL(8f),
|
||||||
|
MEDIUM(16f),
|
||||||
|
LARGE(24f)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class GlanceProviderId(val id: String) {
|
||||||
|
PLAYING_SONG("PLAYING_SONG"),
|
||||||
|
NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"),
|
||||||
|
BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"),
|
||||||
|
CUSTOM_INFO("CUSTOM_INFO"),
|
||||||
|
GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS"),
|
||||||
|
NOTIFICATIONS("NOTIFICATIONS"),
|
||||||
|
GREETINGS("GREETINGS"),
|
||||||
|
EVENTS("EVENTS"),
|
||||||
|
WEATHER("WEATHER");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val map = GlanceProviderId.values().associateBy(GlanceProviderId::id)
|
||||||
|
fun from(type: String) = map[type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class WidgetUpdateFrequency(val rawValue: Int) {
|
||||||
|
LOW(0),
|
||||||
|
DEFAULT(1),
|
||||||
|
HIGH(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class WeatherProvider(val rawValue: Int) {
|
||||||
|
OPEN_WEATHER(0),
|
||||||
|
WEATHER_BIT(1),
|
||||||
|
WEATHER_API(2),
|
||||||
|
HERE(3),
|
||||||
|
ACCUWEATHER(4),
|
||||||
|
WEATHER_GOV(5),
|
||||||
|
YR(6);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val map = WeatherProvider.values().associateBy(WeatherProvider::rawValue)
|
||||||
|
fun fromInt(type: Int) = map[type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class GlanceNotificationTimer(val rawValue: Int) {
|
||||||
|
HALF_MINUTE(0),
|
||||||
|
ONE_MINUTE(1),
|
||||||
|
FIVE_MINUTES(2),
|
||||||
|
TEN_MINUTES(3),
|
||||||
|
FIFTEEN_MINUTES(4),
|
||||||
|
WHEN_DISMISSED(5);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val map = values().associateBy(GlanceNotificationTimer::rawValue)
|
||||||
|
fun fromInt(type: Int) = map[type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class WeatherIconPack(val rawValue: Int) {
|
||||||
|
DEFAULT(0),
|
||||||
|
MINIMAL(1),
|
||||||
|
COOL(2),
|
||||||
|
GOOGLE_NEWS(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class WidgetAlign(val rawValue: Int) {
|
||||||
|
LEFT(0),
|
||||||
|
RIGHT(1),
|
||||||
|
CENTER(2)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,68 +1,154 @@
|
|||||||
package com.tommasoberlose.anotherwidget.global
|
package com.tommasoberlose.anotherwidget.global
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate.*
|
import androidx.appcompat.app.AppCompatDelegate.*
|
||||||
|
import androidx.core.os.ConfigurationCompat
|
||||||
import com.chibatching.kotpref.KotprefModel
|
import com.chibatching.kotpref.KotprefModel
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.isMetric
|
||||||
|
|
||||||
object Preferences : KotprefModel() {
|
object Preferences : KotprefModel() {
|
||||||
override val commitAllPropertiesByDefault: Boolean = true
|
override val commitAllPropertiesByDefault: Boolean = true
|
||||||
|
|
||||||
var darkThemePreference by intPref(default = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) MODE_NIGHT_FOLLOW_SYSTEM else MODE_NIGHT_AUTO_BATTERY)
|
var darkThemePreference by intPref(default = MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
|
||||||
|
// Calendar and weather
|
||||||
var showEvents by booleanPref(key = "PREF_SHOW_EVENTS", default = false)
|
var showEvents by booleanPref(key = "PREF_SHOW_EVENTS", default = false)
|
||||||
var showWeather by booleanPref(key = "PREF_SHOW_WEATHER")
|
var showWeather by booleanPref(key = "PREF_SHOW_WEATHER", default = false)
|
||||||
var weatherIcon by stringPref(key = "PREF_WEATHER_ICON")
|
var weatherIcon by stringPref(key = "PREF_WEATHER_ICON", default = "")
|
||||||
var weatherTemp by floatPref(key = "PREF_WEATHER_TEMP")
|
var weatherTemp by floatPref(key = "PREF_WEATHER_TEMP", default = 0f)
|
||||||
var weatherTempUnit by stringPref(key = "PREF_WEATHER_TEMP_UNIT", default = "F")
|
var weatherTempUnit by stringPref(key = "PREF_WEATHER_TEMP_UNIT", default = if (ConfigurationCompat.getLocales(context.resources.configuration)[0].isMetric()) "C" else "F")
|
||||||
var weatherRealTempUnit by stringPref(key = "PREF_WEATHER_REAL_TEMP_UNIT", default = "F")
|
var weatherRealTempUnit by stringPref(key = "PREF_WEATHER_REAL_TEMP_UNIT", default = if (ConfigurationCompat.getLocales(context.resources.configuration)[0].isMetric()) "C" else "F")
|
||||||
var calendarAllDay by booleanPref(key = "PREF_CALENDAR_ALL_DAY", default = false)
|
var calendarAllDay by booleanPref(key = "PREF_CALENDAR_ALL_DAY", default = true)
|
||||||
var calendarFilter by stringPref(key = "PREF_CALENDAR_FILTER", default = "")
|
var calendarFilter by stringPref(key = "PREF_CALENDAR_FILTER", default = "")
|
||||||
|
|
||||||
var eventId by intPref(key = "PREF_EVENT_ID", default = -1)
|
|
||||||
var nextEventId by longPref(key = "PREF_NEXT_EVENT_ID", default = -1)
|
var nextEventId by longPref(key = "PREF_NEXT_EVENT_ID", default = -1)
|
||||||
var nextEventName by stringPref(key = "PREF_NEXT_EVENT_NAME")
|
|
||||||
var nextEventStartDate by longPref(key = "PREF_NEXT_EVENT_START_DATE")
|
|
||||||
var nextEventAllDay by booleanPref(key = "PREF_NEXT_EVENT_ALL_DAY")
|
|
||||||
var nextEventLocation by stringPref(key = "PREF_NEXT_EVENT_LOCATION")
|
|
||||||
var nextEventEndDate by longPref(key = "PREF_NEXT_EVENT_END_DATE")
|
|
||||||
var nextEventCalendarId by intPref(key = "PREF_NEXT_EVENT_CALENDAR_ID")
|
|
||||||
var customLocationLat by stringPref(key = "PREF_CUSTOM_LOCATION_LAT", default = "")
|
var customLocationLat by stringPref(key = "PREF_CUSTOM_LOCATION_LAT", default = "")
|
||||||
var customLocationLon by stringPref(key = "PREF_CUSTOM_LOCATION_LON", default = "")
|
var customLocationLon by stringPref(key = "PREF_CUSTOM_LOCATION_LON", default = "")
|
||||||
var customLocationAdd by stringPref(key = "PREF_CUSTOM_LOCATION_ADD", default = "")
|
var customLocationAdd by stringPref(key = "PREF_CUSTOM_LOCATION_ADD", default = "")
|
||||||
var hourFormat by stringPref(key = "PREF_HOUR_FORMAT", default = "12")
|
|
||||||
var dateFormat by stringPref(default = "")
|
var dateFormat by stringPref(default = "")
|
||||||
|
var isDateCapitalize by booleanPref(default = true)
|
||||||
|
var isDateUppercase by booleanPref(default = false)
|
||||||
var weatherRefreshPeriod by intPref(key = "PREF_WEATHER_REFRESH_PERIOD", default = 1)
|
var weatherRefreshPeriod by intPref(key = "PREF_WEATHER_REFRESH_PERIOD", default = 1)
|
||||||
var showUntil by intPref(key = "PREF_SHOW_UNTIL", default = 1)
|
var showUntil by intPref(key = "PREF_SHOW_UNTIL", default = 1)
|
||||||
var calendarAppName by stringPref(key = "PREF_CALENDAR_APP_NAME", default = "")
|
var calendarAppName by stringPref(key = "PREF_CALENDAR_APP_NAME", default = "")
|
||||||
var calendarAppPackage by stringPref(key = "PREF_CALENDAR_APP_PACKAGE", default = "")
|
var calendarAppPackage by stringPref(key = "PREF_CALENDAR_APP_PACKAGE", default = "")
|
||||||
var weatherAppName by stringPref(key = "PREF_WEATHER_APP_NAME", default = "")
|
var weatherAppName by stringPref(key = "PREF_WEATHER_APP_NAME", default = "")
|
||||||
var weatherAppPackage by stringPref(key = "PREF_WEATHER_APP_PACKAGE", default = "")
|
var weatherAppPackage by stringPref(key = "PREF_WEATHER_APP_PACKAGE", default = "")
|
||||||
var weatherProviderApi by stringPref(key = "PREF_WEATHER_PROVIDER_API_KEY", default = "")
|
var weatherProviderApiOpen by stringPref(key = "PREF_WEATHER_PROVIDER_API_KEY", default = "")
|
||||||
|
var weatherProviderApiHere by stringPref(default = "")
|
||||||
|
var weatherProviderApiWeatherApi by stringPref(default = "")
|
||||||
|
var weatherProviderApiWeatherBit by stringPref(default = "")
|
||||||
|
var weatherProviderApiAccuweather by stringPref(default = "")
|
||||||
|
var weatherProvider by intPref(default = if (ConfigurationCompat.getLocales(context.resources.configuration)[0].isMetric()) Constants.WeatherProvider.YR.rawValue else Constants.WeatherProvider.WEATHER_GOV.rawValue)
|
||||||
|
var weatherProviderError by stringPref(default = "")
|
||||||
|
var weatherProviderLocationError by stringPref(default = "")
|
||||||
var eventAppName by stringPref(key = "PREF_EVENT_APP_NAME", default = "")
|
var eventAppName by stringPref(key = "PREF_EVENT_APP_NAME", default = "")
|
||||||
var eventAppPackage by stringPref(key = "PREF_EVENT_APP_PACKAGE", default = "")
|
var eventAppPackage by stringPref(key = "PREF_EVENT_APP_PACKAGE", default = "")
|
||||||
var showEventLocation by stringPref(key = "PREF_SHOW_EVENT_LOCATION", default = "")
|
var openEventDetails by booleanPref(default = true)
|
||||||
|
|
||||||
|
var widgetUpdateFrequency by intPref(default = Constants.WidgetUpdateFrequency.DEFAULT.rawValue)
|
||||||
|
|
||||||
var textGlobalColor by stringPref(key = "PREF_TEXT_COLOR", default = "#FFFFFF")
|
var textGlobalColor by stringPref(key = "PREF_TEXT_COLOR", default = "#FFFFFF")
|
||||||
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f)
|
var textGlobalAlpha by stringPref(default = "FF")
|
||||||
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f)
|
|
||||||
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f)
|
var textSecondaryColor by stringPref(default = "#FFFFFF")
|
||||||
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.value)
|
var textSecondaryAlpha by stringPref(default = "FF")
|
||||||
|
|
||||||
|
var backgroundCardColor by stringPref(default = "#000000")
|
||||||
|
var backgroundCardAlpha by stringPref(default = "00")
|
||||||
|
|
||||||
|
var clockTextColor by stringPref(default = "#FFFFFF")
|
||||||
|
var clockTextAlpha by stringPref(default = "FF")
|
||||||
|
|
||||||
|
var textGlobalColorDark by stringPref(default = "#FFFFFF")
|
||||||
|
var textGlobalAlphaDark by stringPref(default = "FF")
|
||||||
|
|
||||||
|
var textSecondaryColorDark by stringPref(default = "#FFFFFF")
|
||||||
|
var textSecondaryAlphaDark by stringPref(default = "FF")
|
||||||
|
|
||||||
|
var backgroundCardColorDark by stringPref(default = "#000000")
|
||||||
|
var backgroundCardAlphaDark by stringPref(default = "00")
|
||||||
|
|
||||||
|
var clockTextColorDark by stringPref(default = "#FFFFFF")
|
||||||
|
var clockTextAlphaDark by stringPref(default = "FF")
|
||||||
|
|
||||||
|
|
||||||
|
var showAMPMIndicator by booleanPref(default = true)
|
||||||
|
|
||||||
|
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.rawValue)
|
||||||
|
|
||||||
|
// UI
|
||||||
|
var widgetMargin by floatPref(default = Constants.Dimension.NONE.rawValue)
|
||||||
|
var widgetPadding by floatPref(default = Constants.Dimension.SMALL.rawValue)
|
||||||
|
|
||||||
|
// Clock
|
||||||
|
var altTimezoneLabel by stringPref(default = "")
|
||||||
|
var altTimezoneId by stringPref(default = "")
|
||||||
|
|
||||||
|
// Global
|
||||||
|
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 24f)
|
||||||
|
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 16f)
|
||||||
|
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 72f)
|
||||||
|
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.rawValue)
|
||||||
|
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.SMALL.rawValue)
|
||||||
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
|
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
|
||||||
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
|
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
|
||||||
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
|
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
|
||||||
var showNextAlarm by booleanPref(default = true)
|
|
||||||
var textShadow by intPref(key = "PREF_TEXT_SHADOW", default = 1)
|
var textShadow by intPref(key = "PREF_TEXT_SHADOW", default = 1)
|
||||||
var showDiffTime by booleanPref(key = "PREF_SHOW_DIFF_TIME")
|
var textShadowDark by intPref(default = 1)
|
||||||
var showDeclinedEvents by booleanPref(key = "PREF_SHOW_DECLINED_EVENTS", default = true)
|
var showDiffTime by booleanPref(key = "PREF_SHOW_DIFF_TIME", default = true)
|
||||||
var openWeatherApiKey by stringPref(key = "PREF_OPEN_WEATHER_API_KEY", default = "")
|
var showDeclinedEvents by booleanPref(key = "PREF_SHOW_DECLINED_EVENTS", default = false)
|
||||||
var darkSkyApiKey by stringPref(key = "PREF_DARK_SKY_API_KEY", default = "")
|
var showInvitedEvents by booleanPref(default = false)
|
||||||
var wuApiKey by stringPref(key = "PREF_WU_API_KEY", default = "")
|
var showAcceptedEvents by booleanPref(default = true)
|
||||||
var secondRowInformation by intPref(key = "PREF_SECOND_ROW_INFORMATION", default = 1)
|
var showOnlyBusyEvents by booleanPref(default = false)
|
||||||
var customFont by intPref(key = "PREF_CUSTOM_FONT", default = Constants.CUSTOM_FONT_PRODUCT_SANS)
|
var secondRowInformation by intPref(key = "PREF_SECOND_ROW_INFORMATION", default = 0)
|
||||||
var customFontFile by stringPref(key = "PREF_CUSTOM_FONT_FILE")
|
var customFont by intPref(key = "PREF_CUSTOM_FONT", default = Constants.CUSTOM_FONT_GOOGLE_SANS)
|
||||||
|
var customFontFile by stringPref(default = "")
|
||||||
|
var customFontName by stringPref(default = "")
|
||||||
|
var customFontVariant by stringPref(default = "regular")
|
||||||
var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true)
|
var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true)
|
||||||
var showGpsInformation by booleanPref(key = "PREF_SHOW_GPS_NOTIFICATION", default = true)
|
var showNextEventOnMultipleLines by booleanPref(default = false)
|
||||||
|
|
||||||
var showWallpaper by booleanPref(default = false)
|
var showDividers by booleanPref(default = true)
|
||||||
var showBigClockWarning by booleanPref(default = true)
|
|
||||||
var showWeatherWarning by booleanPref(default = true)
|
var widgetAlign by intPref(default = Constants.WidgetAlign.CENTER.rawValue)
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
var showWallpaper by booleanPref(default = true)
|
||||||
|
var showPreview by booleanPref(default = true)
|
||||||
|
var showXiaomiWarning by booleanPref(default = true)
|
||||||
|
|
||||||
|
// Glance
|
||||||
|
var enabledGlanceProviderOrder by stringPref(default = "")
|
||||||
|
var customNotes by stringPref(default = "")
|
||||||
|
var showNextAlarm by booleanPref(default = false)
|
||||||
|
var showBatteryCharging by booleanPref(default = false)
|
||||||
|
var isBatteryLevelLow by booleanPref(default = false)
|
||||||
|
var isCharging by booleanPref(default = false)
|
||||||
|
var googleFitSteps by longPref(default = -1)
|
||||||
|
var showDailySteps by booleanPref(default = false)
|
||||||
|
var showGreetings by booleanPref(default = false)
|
||||||
|
var showNotifications by booleanPref(default = false)
|
||||||
|
var hideNotificationAfter by intPref(default = Constants.GlanceNotificationTimer.ONE_MINUTE.rawValue)
|
||||||
|
|
||||||
|
var lastNotificationId by intPref(default = -1)
|
||||||
|
var lastNotificationTitle by stringPref(default = "")
|
||||||
|
var lastNotificationIcon by intPref(default = 0)
|
||||||
|
var lastNotificationPackage by stringPref(default = "")
|
||||||
|
|
||||||
|
var showMusic by booleanPref(default = false)
|
||||||
|
var mediaInfoFormat by stringPref(default = MediaPlayerHelper.DEFAULT_MEDIA_INFO_FORMAT)
|
||||||
|
var mediaPlayerTitle by stringPref(default = "")
|
||||||
|
var mediaPlayerAlbum by stringPref(default = "")
|
||||||
|
var mediaPlayerArtist by stringPref(default = "")
|
||||||
|
var mediaPlayerPackage by stringPref(default = IntentHelper.DO_NOTHING_OPTION)
|
||||||
|
var musicPlayersFilter by stringPref(default = "")
|
||||||
|
var appNotificationsFilter by stringPref(default = "")
|
||||||
|
|
||||||
|
var showEventsAsGlanceProvider by booleanPref(default = false)
|
||||||
|
var showWeatherAsGlanceProvider by booleanPref(default = false)
|
||||||
|
|
||||||
|
// Integrations
|
||||||
|
var installedIntegrations by intPref(default = 0)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import com.chibatching.kotpref.Kotpref
|
||||||
|
import com.chibatching.kotpref.blockingBulk
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.receivers.NotificationListener
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
|
||||||
|
object ActiveNotificationsHelper {
|
||||||
|
fun showLastNotification(): Boolean {
|
||||||
|
return Preferences.lastNotificationId != -1 && Preferences.lastNotificationIcon != 0 && Preferences.lastNotificationPackage.isNotBlank() && Preferences.lastNotificationTitle.isNotBlank()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearLastNotification(context: Context) {
|
||||||
|
Kotpref.init(context)
|
||||||
|
Preferences.blockingBulk {
|
||||||
|
remove(Preferences::lastNotificationId)
|
||||||
|
remove(Preferences::lastNotificationTitle)
|
||||||
|
remove(Preferences::lastNotificationPackage)
|
||||||
|
remove(Preferences::lastNotificationIcon)
|
||||||
|
}
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
NotificationListener.clearTimeout(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkNotificationAccess(context: Context): Boolean {
|
||||||
|
val contentResolver: ContentResolver = context.contentResolver
|
||||||
|
val enabledNotificationListeners =
|
||||||
|
Settings.Secure.getString(contentResolver, "enabled_notification_listeners")
|
||||||
|
val packageName: String = context.packageName
|
||||||
|
return NotificationManagerCompat.getEnabledListenerPackages(context).contains(packageName) && (enabledNotificationListeners != null && enabledNotificationListeners.contains(NotificationListener::class.java.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAppAccepted(appPkg: String): Boolean = Preferences.appNotificationsFilter == "" || Preferences.appNotificationsFilter.contains(appPkg)
|
||||||
|
|
||||||
|
fun toggleAppFilter(appPkg: String) {
|
||||||
|
if (Preferences.appNotificationsFilter == "" || !Preferences.appNotificationsFilter.contains(appPkg)) {
|
||||||
|
Preferences.appNotificationsFilter = Preferences.appNotificationsFilter.split(",").union(listOf(appPkg)).joinToString(separator = ",")
|
||||||
|
} else {
|
||||||
|
Preferences.appNotificationsFilter = Preferences.appNotificationsFilter.split(",").filter { it != appPkg }.joinToString(separator = ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,13 @@
|
|||||||
package com.tommasoberlose.anotherwidget.helpers
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
import android.app.AlarmManager
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
|
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -11,9 +16,9 @@ object AlarmHelper {
|
|||||||
val alarm = nextAlarmClock
|
val alarm = nextAlarmClock
|
||||||
return if (
|
return if (
|
||||||
alarm != null
|
alarm != null
|
||||||
&& alarm.triggerTime - Calendar.getInstance().timeInMillis > 2 * 60 * 1000
|
&& alarm.triggerTime - Calendar.getInstance().timeInMillis > 5 * 60 * 1000
|
||||||
&& alarm.triggerTime - Calendar.getInstance().timeInMillis < 12 * 60 * 60 * 1000
|
|
||||||
) {
|
) {
|
||||||
|
setTimeout(context, alarm.triggerTime)
|
||||||
"%s %s".format(
|
"%s %s".format(
|
||||||
SimpleDateFormat("EEE", Locale.getDefault()).format(alarm.triggerTime),
|
SimpleDateFormat("EEE", Locale.getDefault()).format(alarm.triggerTime),
|
||||||
DateFormat.getTimeFormat(context).format(Date(alarm.triggerTime))
|
DateFormat.getTimeFormat(context).format(Date(alarm.triggerTime))
|
||||||
@ -22,4 +27,43 @@ object AlarmHelper {
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isAlarmProbablyWrong(context: Context): Boolean {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val alarm = nextAlarmClock
|
||||||
|
return (
|
||||||
|
alarm != null
|
||||||
|
&& alarm.triggerTime - Calendar.getInstance().timeInMillis < 5 * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTimeout(context: Context, trigger: Long) {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_ALARM_UPDATE
|
||||||
|
}
|
||||||
|
setExactIfCanSchedule(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
trigger,
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
ALARM_UPDATE_ID,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearTimeout(context: Context) {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_ALARM_UPDATE
|
||||||
|
}
|
||||||
|
cancel(PendingIntent.getBroadcast(context, ALARM_UPDATE_ID, intent, PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val ALARM_UPDATE_ID = 24953
|
||||||
}
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Context.BATTERY_SERVICE
|
||||||
|
import android.os.BatteryManager
|
||||||
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
|
||||||
|
|
||||||
|
object BatteryHelper {
|
||||||
|
fun updateBatteryInfo(context: Context) {
|
||||||
|
with(context.getSystemService(BATTERY_SERVICE) as BatteryManager) {
|
||||||
|
Preferences.isBatteryLevelLow = getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) <= 15
|
||||||
|
Preferences.isCharging = isCharging
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBatteryLevel(context: Context): Int {
|
||||||
|
with(context.getSystemService(BATTERY_SERVICE) as BatteryManager) {
|
||||||
|
return getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,40 +2,58 @@ package com.tommasoberlose.anotherwidget.helpers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
object BitmapHelper {
|
object BitmapHelper {
|
||||||
|
|
||||||
fun getBitmapFromView(view: View, width: Int? = null, height: Int? = null, draw: Boolean = true): Bitmap {
|
fun getBitmapFromView(view: View, width: Int? = null, height: Int? = null, draw: Boolean = true): Bitmap {
|
||||||
//Define a bitmap with the same size as the view
|
//Define a bitmap with the same size as the view
|
||||||
val measuredWidth = View.MeasureSpec.makeMeasureSpec(width ?: view.width, if (width != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.UNSPECIFIED)
|
val measuredWidth = View.MeasureSpec.makeMeasureSpec(width ?: view.width, if (width != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.AT_MOST)
|
||||||
val measuredHeight = View.MeasureSpec.makeMeasureSpec(height ?: view.height, if (height != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.UNSPECIFIED)
|
val measuredHeight = View.MeasureSpec.makeMeasureSpec(height ?: view.height, if (height != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.UNSPECIFIED)
|
||||||
view.measure(measuredWidth, measuredHeight)
|
view.measure(
|
||||||
|
if (measuredWidth > 0) measuredWidth else 0,
|
||||||
|
if (measuredHeight > 0) measuredHeight else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
val calculatedWidth = view.measuredWidth
|
||||||
|
val widgetWidth = if (calculatedWidth in 1..16000) {
|
||||||
|
calculatedWidth
|
||||||
|
} else if (width != null && width > 0) {
|
||||||
|
width
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
val calculatedHeight = view.measuredHeight
|
||||||
|
val widgetHeight = if (calculatedHeight in 1..16000) {
|
||||||
|
calculatedHeight
|
||||||
|
} else if (height != null && height > 0) {
|
||||||
|
height
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val btm = Bitmap.createBitmap(
|
val btm = Bitmap.createBitmap(
|
||||||
view.measuredWidth,
|
widgetWidth,
|
||||||
view.measuredHeight,
|
widgetHeight,
|
||||||
Bitmap.Config.ARGB_8888
|
if (draw) Bitmap.Config.ARGB_8888 else Bitmap.Config.ALPHA_8
|
||||||
)
|
)
|
||||||
if (draw) {
|
if (draw) {
|
||||||
//Bind a canvas to it
|
//Bind a canvas to it
|
||||||
val canvas = Canvas(btm)
|
val canvas = Canvas(btm)
|
||||||
// draw the view on the canvas
|
// draw the view on the canvas
|
||||||
view.layout(0, 0, measuredWidth, measuredHeight)
|
view.layout(0, 0, widgetWidth, widgetHeight)
|
||||||
view.draw(canvas)
|
view.draw(canvas)
|
||||||
//return the bitmap
|
//return the bitmap
|
||||||
}
|
}
|
||||||
btm
|
btm
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
FirebaseCrashlytics.getInstance().recordException(ex)
|
Bitmap.createBitmap(5, 5, Bitmap.Config.ALPHA_8)
|
||||||
Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,4 +90,29 @@ object BitmapHelper {
|
|||||||
|
|
||||||
return resultBitmap
|
return resultBitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun drawableToBitmap(drawable: Drawable): Bitmap? {
|
||||||
|
if (drawable is BitmapDrawable) {
|
||||||
|
if (drawable.bitmap != null) {
|
||||||
|
return drawable.bitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val bitmap: Bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
|
||||||
|
Bitmap.createBitmap(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Bitmap.Config.ARGB_8888
|
||||||
|
) // Single color bitmap will be created of 1x1 pixel
|
||||||
|
} else {
|
||||||
|
Bitmap.createBitmap(
|
||||||
|
drawable.intrinsicWidth,
|
||||||
|
drawable.intrinsicHeight,
|
||||||
|
Bitmap.Config.ARGB_8888
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val canvas = Canvas(bitmap)
|
||||||
|
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||||
|
drawable.draw(canvas)
|
||||||
|
return bitmap
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,22 +1,15 @@
|
|||||||
package com.tommasoberlose.anotherwidget.helpers
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ContentUris
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.provider.CalendarContract
|
import android.provider.CalendarContract
|
||||||
import android.util.Log
|
|
||||||
import com.tommasoberlose.anotherwidget.services.EventListenerJob
|
|
||||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
|
||||||
import com.tommasoberlose.anotherwidget.models.Event
|
import com.tommasoberlose.anotherwidget.models.Event
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
import com.tommasoberlose.anotherwidget.services.UpdateCalendarWorker
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
import me.everything.providers.android.calendar.CalendarProvider
|
import me.everything.providers.android.calendar.CalendarProvider
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.Comparator
|
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,110 +17,8 @@ import kotlin.collections.ArrayList
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
object CalendarHelper {
|
object CalendarHelper {
|
||||||
|
|
||||||
fun updateEventList(context: Context) {
|
fun updateEventList(context: Context) {
|
||||||
val eventRepository = EventRepository(context)
|
UpdateCalendarWorker.enqueue(context)
|
||||||
if (Preferences.showEvents) {
|
|
||||||
val eventList = ArrayList<Event>()
|
|
||||||
|
|
||||||
val now = Calendar.getInstance()
|
|
||||||
val limit = Calendar.getInstance()
|
|
||||||
when (Preferences.showUntil) {
|
|
||||||
0 -> limit.add(Calendar.HOUR, 3)
|
|
||||||
1 -> limit.add(Calendar.HOUR, 6)
|
|
||||||
2 -> limit.add(Calendar.HOUR, 12)
|
|
||||||
3 -> limit.add(Calendar.DAY_OF_MONTH, 1)
|
|
||||||
4 -> limit.add(Calendar.DAY_OF_MONTH, 3)
|
|
||||||
5 -> limit.add(Calendar.DAY_OF_MONTH, 7)
|
|
||||||
6 -> limit.add(Calendar.MINUTE, 30)
|
|
||||||
7 -> limit.add(Calendar.HOUR, 1)
|
|
||||||
else -> limit.add(Calendar.HOUR, 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
|
|
||||||
ContentUris.appendId(builder, now.timeInMillis)
|
|
||||||
ContentUris.appendId(builder, limit.timeInMillis)
|
|
||||||
|
|
||||||
if (!context.checkGrantedPermission(
|
|
||||||
Manifest.permission.READ_CALENDAR
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
eventRepository.resetNextEventData()
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
val provider = CalendarProvider(context)
|
|
||||||
val data = provider.getInstances(now.timeInMillis, limit.timeInMillis)
|
|
||||||
if (data != null) {
|
|
||||||
val instances = data.list
|
|
||||||
for (instance in instances) {
|
|
||||||
try {
|
|
||||||
val e = provider.getEvent(instance.eventId)
|
|
||||||
if (e != null && !e.deleted && instance.begin <= limit.timeInMillis && (Preferences.calendarAllDay || !e.allDay) && !getFilteredCalendarIdList().contains(
|
|
||||||
e.calendarId
|
|
||||||
) && (Preferences.showDeclinedEvents || e.selfAttendeeStatus.toInt() != CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED)
|
|
||||||
) {
|
|
||||||
if (e.allDay) {
|
|
||||||
val start = Calendar.getInstance()
|
|
||||||
start.timeInMillis = instance.begin
|
|
||||||
val end = Calendar.getInstance()
|
|
||||||
end.timeInMillis = instance.end
|
|
||||||
instance.begin =
|
|
||||||
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
|
|
||||||
instance.end =
|
|
||||||
end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
|
|
||||||
}
|
|
||||||
eventList.add(
|
|
||||||
Event(
|
|
||||||
instance.id,
|
|
||||||
e.id,
|
|
||||||
e.title ?: "",
|
|
||||||
instance.begin,
|
|
||||||
instance.end,
|
|
||||||
e.calendarId.toInt(),
|
|
||||||
e.allDay,
|
|
||||||
e.eventLocation ?: ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (ignored: Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventList.isEmpty()) {
|
|
||||||
eventRepository.resetNextEventData()
|
|
||||||
} else {
|
|
||||||
eventList.sortWith(Comparator { event: Event, event1: Event ->
|
|
||||||
if (event.allDay && event1.allDay) {
|
|
||||||
event.startDate.compareTo(event1.startDate)
|
|
||||||
} else if (event.allDay) {
|
|
||||||
1
|
|
||||||
} else if (event1.allDay) {
|
|
||||||
-1
|
|
||||||
} else {
|
|
||||||
event1.startDate.compareTo(event.startDate)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
eventList.reverse()
|
|
||||||
eventRepository.saveEvents(
|
|
||||||
eventList
|
|
||||||
)
|
|
||||||
eventRepository.saveNextEventData(
|
|
||||||
eventList[0]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (ignored: java.lang.Exception) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eventRepository.resetNextEventData()
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdatesReceiver.setUpdates(context)
|
|
||||||
MainWidget.updateWidget(context)
|
|
||||||
|
|
||||||
EventBus.getDefault().post(MainActivity.UpdateUiMessageEvent())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {
|
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {
|
||||||
@ -149,7 +40,8 @@ object CalendarHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getFilteredCalendarIdList(): List<Long> {
|
fun getFilteredCalendarIdList(): List<Long> {
|
||||||
return Preferences.calendarFilter.split(",").map { it.replace(" ", "") }.filter { it != "" }.map { it.toLong() }
|
return Preferences.calendarFilter.split(",").map { it.replace(" ", "") }
|
||||||
|
.filter { it != "" }.map { it.toLong() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterCalendar(list: List<Long>) {
|
fun filterCalendar(list: List<Long>) {
|
||||||
@ -157,10 +49,44 @@ object CalendarHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setEventUpdatesAndroidN(context: Context) {
|
fun setEventUpdatesAndroidN(context: Context) {
|
||||||
EventListenerJob.schedule(context)
|
UpdateCalendarWorker.enqueueTrigger(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeEventUpdatesAndroidN(context: Context) {
|
fun removeEventUpdatesAndroidN(context: Context) {
|
||||||
EventListenerJob.remove(context)
|
UpdateCalendarWorker.cancelTrigger(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Event>.applyFilters() : List<Event> {
|
||||||
|
return this
|
||||||
|
.asSequence()
|
||||||
|
.filter { (Preferences.showDeclinedEvents || it.selfAttendeeStatus != CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED) }
|
||||||
|
.filter { (Preferences.showAcceptedEvents || it.selfAttendeeStatus != CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED) }
|
||||||
|
.filter { (Preferences.showInvitedEvents || it.selfAttendeeStatus != CalendarContract.Attendees.ATTENDEE_STATUS_INVITED) }
|
||||||
|
.filter { (Preferences.calendarAllDay || !it.allDay) }
|
||||||
|
.filter { (!Preferences.showOnlyBusyEvents || it.availability != CalendarContract.EventsEntity.AVAILABILITY_FREE) }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Event>.sortEvents(): List<Event> {
|
||||||
|
return sortedWith { event: Event, event1: Event ->
|
||||||
|
val date = Calendar.getInstance().apply { timeInMillis = event.startDate }
|
||||||
|
val date1 = Calendar.getInstance().apply { timeInMillis = event1.startDate }
|
||||||
|
|
||||||
|
if (date.get(Calendar.DAY_OF_YEAR) == date1.get(Calendar.DAY_OF_YEAR) && date.get(
|
||||||
|
Calendar.YEAR) == date1.get(Calendar.YEAR)
|
||||||
|
) {
|
||||||
|
if (event.allDay && event1.allDay) {
|
||||||
|
event.startDate.compareTo(event1.startDate)
|
||||||
|
} else if (event.allDay) {
|
||||||
|
1
|
||||||
|
} else if (event1.allDay) {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
event.startDate.compareTo(event1.startDate)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.startDate.compareTo(event1.startDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,126 @@
|
|||||||
package com.tommasoberlose.anotherwidget.helpers
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Context.CLIPBOARD_SERVICE
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
object ColorHelper {
|
object ColorHelper {
|
||||||
fun getFontColor(): Int {
|
fun getFontColor(isDark: Boolean): Int {
|
||||||
return try {
|
return try {
|
||||||
Color.parseColor(Preferences.textGlobalColor)
|
Color.parseColor("#%s%s".format(if (!isDark) Preferences.textGlobalAlpha else Preferences.textGlobalAlphaDark,
|
||||||
|
(if (!isDark) Preferences.textGlobalColor else Preferences.textGlobalColorDark).replace(
|
||||||
|
"#",
|
||||||
|
"")))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Color.parseColor("#FFFFFF")
|
Color.parseColor("#FFFFFFFF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFontColorAlpha(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
(if (!isDark) Preferences.textGlobalAlpha else Preferences.textGlobalAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"FF".toIntValue().toDouble() * 255 / 100
|
||||||
|
}.roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFontColorRgb(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor((if (!isDark) Preferences.textGlobalColor else Preferences.textGlobalColorDark))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#000000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSecondaryFontColor(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor("#%s%s".format((if (!isDark) Preferences.textSecondaryAlpha else Preferences.textSecondaryAlphaDark),
|
||||||
|
(if (!isDark) Preferences.textSecondaryColor else Preferences.textSecondaryColorDark).replace(
|
||||||
|
"#",
|
||||||
|
"")))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#FFFFFFFF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSecondaryFontColorAlpha(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
(if (!isDark) Preferences.textSecondaryAlpha else Preferences.textSecondaryAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"FF".toIntValue().toDouble() * 255 / 100
|
||||||
|
}.roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSecondaryFontColorRgb(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor((if (!isDark) Preferences.textSecondaryColor else Preferences.textSecondaryColorDark))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#000000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClockFontColor(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor("#%s%s".format((if (!isDark) Preferences.clockTextAlpha else Preferences.clockTextAlphaDark),
|
||||||
|
(if (!isDark) Preferences.clockTextColor else Preferences.clockTextColorDark).replace(
|
||||||
|
"#",
|
||||||
|
"")))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#FFFFFFFF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClockFontColorAlpha(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
(if (!isDark) Preferences.clockTextAlpha else Preferences.clockTextAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"FF".toIntValue().toDouble() * 255 / 100
|
||||||
|
}.roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClockFontColorRgb(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor((if (!isDark) Preferences.clockTextColor else Preferences.clockTextColorDark))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#000000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBackgroundColor(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor("#%s%s".format((if (!isDark) Preferences.backgroundCardAlpha else Preferences.backgroundCardAlphaDark),
|
||||||
|
(if (!isDark) Preferences.backgroundCardColor else Preferences.backgroundCardColorDark).replace(
|
||||||
|
"#",
|
||||||
|
"")))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#00000000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBackgroundAlpha(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
(if (!isDark) Preferences.backgroundCardAlpha else Preferences.backgroundCardAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"00".toIntValue().toDouble() * 255 / 100
|
||||||
|
}.roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBackgroundColorRgb(isDark: Boolean): Int {
|
||||||
|
return try {
|
||||||
|
Color.parseColor((if (!isDark) Preferences.backgroundCardColor else Preferences.backgroundCardColorDark))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Color.parseColor("#000000")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,4 +132,62 @@ object ColorHelper {
|
|||||||
1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
|
1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
|
||||||
return darkness >= threshold
|
return darkness >= threshold
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
fun Int.toHexValue(): String {
|
||||||
|
val intValue = (this * 255 / 100).toDouble().roundToInt()
|
||||||
|
val hexValue = intValue.toString(16)
|
||||||
|
return hexValue.padStart(2, '0').toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.toIntValue(): Int {
|
||||||
|
val hexValue = this.toInt(16).toDouble()
|
||||||
|
return (hexValue * 100 / 255).roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.isColor(): Boolean {
|
||||||
|
return try {
|
||||||
|
Color.parseColor(this)
|
||||||
|
true
|
||||||
|
} catch (iae: IllegalArgumentException) {
|
||||||
|
iae.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
fun Context.copyToClipboard(color: Int?, alpha: Int) {
|
||||||
|
if (color == null) return toast(getString(R.string.error_copy_color))
|
||||||
|
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
|
||||||
|
try {
|
||||||
|
val colorString = Integer.toHexString(color)
|
||||||
|
val clip = "#%s%s".format(
|
||||||
|
alpha.toHexValue(),
|
||||||
|
if (colorString.length > 6) colorString.substring(2) else colorString
|
||||||
|
).toUpperCase()
|
||||||
|
setPrimaryClip(ClipData.newPlainText(clip, clip))
|
||||||
|
toast(getString(R.string.color_copied))
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
toast(getString(R.string.error_copy_color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.isClipboardColor(): Boolean {
|
||||||
|
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
|
||||||
|
return try { primaryClip?.getItemAt(0)?.text?.toString()?.isColor() ?: false } catch (ex: Exception) { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.pasteFromClipboard(pasteColor: (color: String, alpha: String) -> Unit) {
|
||||||
|
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
|
||||||
|
primaryClip?.let {
|
||||||
|
val item = it.getItemAt(0).text.toString().replace("#", "")
|
||||||
|
val color = if (item.length > 6) item.substring(2) else item
|
||||||
|
val alpha = if (item.length > 6) item.substring(0, 2) else "00"
|
||||||
|
pasteColor("#$color", alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ package com.tommasoberlose.anotherwidget.helpers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.util.Log
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
@ -12,18 +11,28 @@ import java.util.*
|
|||||||
object DateHelper {
|
object DateHelper {
|
||||||
fun getDateText(context: Context, date: Calendar): String {
|
fun getDateText(context: Context, date: Calendar): String {
|
||||||
return if (Preferences.dateFormat != "") {
|
return if (Preferences.dateFormat != "") {
|
||||||
try {
|
val text = try {
|
||||||
SimpleDateFormat(Preferences.dateFormat, Locale.getDefault()).format(date.time)
|
SimpleDateFormat(Preferences.dateFormat, Locale.getDefault()).format(date.time)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
getDefaultDateText(context, date)
|
getDefaultDateText(context, date)
|
||||||
}
|
}
|
||||||
|
when {
|
||||||
|
Preferences.isDateUppercase -> text.toUpperCase(Locale.getDefault())
|
||||||
|
Preferences.isDateCapitalize -> text.getCapWordString()
|
||||||
|
else -> text
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val flags: Int =
|
val flags: Int =
|
||||||
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
|
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
|
||||||
"%s, %s".format(
|
val text = "%s, %s".format(
|
||||||
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
|
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
|
||||||
DateUtils.formatDateTime(context, date.timeInMillis, flags)
|
DateUtils.formatDateTime(context, date.timeInMillis, flags)
|
||||||
).getCapWordString()
|
)
|
||||||
|
when {
|
||||||
|
Preferences.isDateUppercase -> text.toUpperCase(Locale.getDefault())
|
||||||
|
Preferences.isDateCapitalize -> text.getCapWordString()
|
||||||
|
else -> text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +42,6 @@ object DateHelper {
|
|||||||
return "%s, %s".format(
|
return "%s, %s".format(
|
||||||
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
|
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
|
||||||
DateUtils.formatDateTime(context, date.timeInMillis, flags)
|
DateUtils.formatDateTime(context, date.timeInMillis, flags)
|
||||||
).getCapWordString()
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.models.GlanceProvider
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
object GlanceProviderHelper {
|
||||||
|
fun getGlanceProviders(context: Context): ArrayList<Constants.GlanceProviderId> {
|
||||||
|
val enabledProviders = Preferences.enabledGlanceProviderOrder.split(",").filter { it != "" }
|
||||||
|
|
||||||
|
val providers = Constants.GlanceProviderId.values()
|
||||||
|
.filter {
|
||||||
|
context.checkIfFitInstalled() || it != Constants.GlanceProviderId.GOOGLE_FIT_STEPS
|
||||||
|
}
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
|
return ArrayList(providers.filter { enabledProviders.contains(it.id) }.sortedWith(Comparator { p1, p2 ->
|
||||||
|
when {
|
||||||
|
enabledProviders.contains(p1.id) && enabledProviders.contains(p2.id) -> {
|
||||||
|
enabledProviders.indexOf(p1.id).compareTo(enabledProviders.indexOf(p2.id))
|
||||||
|
}
|
||||||
|
enabledProviders.contains(p1.id) -> {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
enabledProviders.contains(p2.id) -> {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
p1.id.compareTo(p2.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) + providers.filter { !enabledProviders.contains(it.id) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGlanceProviderById(context: Context, providerId: Constants.GlanceProviderId): GlanceProvider? {
|
||||||
|
return when(providerId) {
|
||||||
|
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_show_next_alarm_title),
|
||||||
|
R.drawable.round_access_alarm_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.PLAYING_SONG -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_show_music_title),
|
||||||
|
R.drawable.round_music_note_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.CUSTOM_INFO -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_custom_notes_title),
|
||||||
|
R.drawable.round_sticky_note_2_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_low_battery_level_title),
|
||||||
|
R.drawable.round_battery_charging_full_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_daily_steps_title),
|
||||||
|
R.drawable.round_favorite_border_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.NOTIFICATIONS -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_show_notifications_title),
|
||||||
|
R.drawable.round_notifications_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.GREETINGS -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_show_greetings_title),
|
||||||
|
R.drawable.round_history_edu_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.EVENTS -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_show_events_as_glance_provider_title),
|
||||||
|
R.drawable.round_event_note_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Constants.GlanceProviderId.WEATHER -> {
|
||||||
|
GlanceProvider(providerId.id,
|
||||||
|
context.getString(R.string.settings_show_weather_as_glance_provider_title),
|
||||||
|
R.drawable.round_brightness_5_24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveGlanceProviderOrder(list: List<Constants.GlanceProviderId>) {
|
||||||
|
Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showGlanceProviders(context: Context): Boolean {
|
||||||
|
val eventRepository = EventRepository(context)
|
||||||
|
BatteryHelper.updateBatteryInfo(context)
|
||||||
|
|
||||||
|
val showGlance = (eventRepository.getEventsCount() == 0 || !Preferences.showEvents || Preferences.showEventsAsGlanceProvider)
|
||||||
|
&& (
|
||||||
|
(Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) ||
|
||||||
|
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
|
||||||
|
(MediaPlayerHelper.isSomeonePlaying(context)) ||
|
||||||
|
(Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) ||
|
||||||
|
(Preferences.customNotes.isNotEmpty()) ||
|
||||||
|
(Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") ||
|
||||||
|
(Preferences.showDailySteps && Preferences.googleFitSteps > 0) ||
|
||||||
|
(Preferences.showGreetings && GreetingsHelper.showGreetings()) ||
|
||||||
|
(Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
|
||||||
|
Manifest.permission.READ_CALENDAR) && eventRepository.getNextEvent() != null)
|
||||||
|
)
|
||||||
|
eventRepository.close()
|
||||||
|
return showGlance
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
object GreetingsHelper {
|
||||||
|
private const val MORNING_TIME = 36
|
||||||
|
private const val MORNING_TIME_END = 37
|
||||||
|
private const val EVENING_TIME = 38
|
||||||
|
private const val NIGHT_TIME = 39
|
||||||
|
|
||||||
|
fun toggleGreetings(context: Context) {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val now = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
}
|
||||||
|
if (Preferences.showGreetings) {
|
||||||
|
setRepeating(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
now.apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 5)
|
||||||
|
}.timeInMillis,
|
||||||
|
1000 * 60 * 60 * 24,
|
||||||
|
PendingIntent.getBroadcast(context,
|
||||||
|
MORNING_TIME,
|
||||||
|
Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_UPDATE_GREETINGS
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
setRepeating(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
now.apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 9)
|
||||||
|
}.timeInMillis,
|
||||||
|
1000 * 60 * 60 * 24,
|
||||||
|
PendingIntent.getBroadcast(context,
|
||||||
|
MORNING_TIME_END,
|
||||||
|
Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_UPDATE_GREETINGS
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
setRepeating(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
now.apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 19)
|
||||||
|
}.timeInMillis,
|
||||||
|
1000 * 60 * 60 * 24,
|
||||||
|
PendingIntent.getBroadcast(context,
|
||||||
|
EVENING_TIME,
|
||||||
|
Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_UPDATE_GREETINGS
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
setRepeating(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
now.apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 22)
|
||||||
|
}.timeInMillis,
|
||||||
|
1000 * 60 * 60 * 24,
|
||||||
|
PendingIntent.getBroadcast(context,
|
||||||
|
NIGHT_TIME,
|
||||||
|
Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_UPDATE_GREETINGS
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
listOf(MORNING_TIME, MORNING_TIME_END, EVENING_TIME, NIGHT_TIME).forEach {
|
||||||
|
cancel(PendingIntent.getBroadcast(context, it, Intent(context,
|
||||||
|
UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_UPDATE_GREETINGS
|
||||||
|
}, PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showGreetings(): Boolean {
|
||||||
|
val hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
|
||||||
|
return hour < 9 || hour >= 19
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRandomString(context: Context): String {
|
||||||
|
val hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
|
||||||
|
val array = when {
|
||||||
|
hour in 5..8 -> context.resources.getStringArray(R.array.morning_greetings)
|
||||||
|
hour in 19..21 -> context.resources.getStringArray(R.array.evening_greetings)
|
||||||
|
hour >= 22 || hour < 5 -> context.resources.getStringArray(R.array.night_greetings)
|
||||||
|
else -> emptyArray()
|
||||||
|
}
|
||||||
|
return if (array.isNotEmpty()) array[Random().nextInt(array.size)] else ""
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.*
|
||||||
|
import android.renderscript.*
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||||
|
import java.util.prefs.Preferences
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
object ImageHelper {
|
||||||
|
fun ImageView.applyShadow(originalView: ImageView, factor: Float = 1f) {
|
||||||
|
clearColorFilter()
|
||||||
|
val cElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, when (if (context.isDarkTheme()) com.tommasoberlose.anotherwidget.global.Preferences.textShadowDark else com.tommasoberlose.anotherwidget.global.Preferences.textShadow) {
|
||||||
|
0 -> 0f * factor
|
||||||
|
1 -> 8f * factor
|
||||||
|
2 -> 16f * factor
|
||||||
|
else -> 8f * factor
|
||||||
|
}, resources.displayMetrics)
|
||||||
|
|
||||||
|
if (originalView.drawable != null && originalView.drawable.intrinsicWidth > 0 && originalView.drawable.intrinsicHeight > 0) {
|
||||||
|
val btm = originalView.drawable.toBitmap().copy(Bitmap.Config.ARGB_8888, true)
|
||||||
|
val comb = Bitmap.createBitmap(btm)
|
||||||
|
val shadowBitmap = generateShadowBitmap(context, cElevation, btm, factor)
|
||||||
|
|
||||||
|
shadowBitmap?.let {
|
||||||
|
val canvas = Canvas(comb)
|
||||||
|
canvas.drawColor(Color.TRANSPARENT)
|
||||||
|
canvas.save()
|
||||||
|
val rect = Rect()
|
||||||
|
// val bounds = originalView.drawable.copyBounds()
|
||||||
|
canvas.getClipBounds(rect)
|
||||||
|
rect.inset(-2 * getBlurRadius(context, cElevation).toInt(), -2 * getBlurRadius(context, cElevation).toInt())
|
||||||
|
canvas.save()
|
||||||
|
canvas.clipRect(rect)
|
||||||
|
canvas.drawBitmap(shadowBitmap, 0f, 2f, null)
|
||||||
|
canvas.restore()
|
||||||
|
setImageBitmap(comb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateShadowBitmap(context: Context, cElevation: Float, bitmap: Bitmap?, factor: Float): Bitmap? {
|
||||||
|
val rs: RenderScript = RenderScript.create(context)
|
||||||
|
val element = Element.U8_4(rs)
|
||||||
|
val blurScript: ScriptIntrinsicBlur = ScriptIntrinsicBlur.create(rs, element)
|
||||||
|
val colorMatrixScript: ScriptIntrinsicColorMatrix = ScriptIntrinsicColorMatrix.create(rs)
|
||||||
|
val allocationIn = Allocation.createFromBitmap(rs, bitmap)
|
||||||
|
val allocationOut = Allocation.createTyped(rs, allocationIn.type)
|
||||||
|
|
||||||
|
val matrix = Matrix4f(floatArrayOf(
|
||||||
|
0f, 0f, 0f, 0f,
|
||||||
|
0f, 0f, 0f, 0f,
|
||||||
|
0f, 0f, 0f, 0f,
|
||||||
|
0f, 0f, 0f, when (if (context.isDarkTheme()) com.tommasoberlose.anotherwidget.global.Preferences.textShadowDark else com.tommasoberlose.anotherwidget.global.Preferences.textShadow) {
|
||||||
|
0 -> 0f * factor
|
||||||
|
1 -> 0.8f * factor
|
||||||
|
2 -> 1f * factor
|
||||||
|
else -> 0.8f * factor
|
||||||
|
}))
|
||||||
|
|
||||||
|
colorMatrixScript.setColorMatrix(matrix)
|
||||||
|
colorMatrixScript.forEach(allocationIn, allocationOut)
|
||||||
|
|
||||||
|
blurScript.setRadius(getBlurRadius(context, cElevation))
|
||||||
|
|
||||||
|
blurScript.setInput(allocationOut)
|
||||||
|
blurScript.forEach(allocationIn)
|
||||||
|
|
||||||
|
allocationIn.copyTo(bitmap)
|
||||||
|
|
||||||
|
allocationIn.destroy()
|
||||||
|
allocationOut.destroy()
|
||||||
|
colorMatrixScript.destroy()
|
||||||
|
blurScript.destroy()
|
||||||
|
//rs.destroy()
|
||||||
|
|
||||||
|
return bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBlurRadius(context: Context, customElevation: Float): Float {
|
||||||
|
val maxElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f, context.resources.displayMetrics)
|
||||||
|
return min(25f * (customElevation / maxElevation), 25f)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package com.tommasoberlose.anotherwidget.helpers
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -10,68 +12,74 @@ import android.provider.AlarmClock
|
|||||||
import android.provider.CalendarContract
|
import android.provider.CalendarContract
|
||||||
import android.provider.CalendarContract.Events
|
import android.provider.CalendarContract.Events
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.models.Event
|
import com.tommasoberlose.anotherwidget.models.Event
|
||||||
|
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
object IntentHelper {
|
object IntentHelper {
|
||||||
|
|
||||||
fun getGoogleMapsIntentFromAddress(context: Context, address:String): Intent {
|
const val DEFAULT_OPTION = ""
|
||||||
val gmmIntentUri: Uri = Uri.parse("geo:0,0?q=$address")
|
const val DO_NOTHING_OPTION = "DO_NOTHING"
|
||||||
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
|
const val REFRESH_WIDGET_OPTION = "REFRESH_WIDGET"
|
||||||
mapIntent.`package` = "com.google.android.apps.maps"
|
|
||||||
|
|
||||||
return if (mapIntent.resolveActivity(context.packageManager) != null) {
|
fun getPendingIntent(context: Context, requestCode: Int, intent: Intent, flags: Int): PendingIntent {
|
||||||
mapIntent
|
return if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK == Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
} else {
|
PendingIntent.getActivity(context, requestCode, intent, flags)
|
||||||
val map = "http://maps.google.co.in/maps?q=$address"
|
else
|
||||||
val i = Intent(Intent.ACTION_VIEW, Uri.parse(map));
|
PendingIntent.getBroadcast(context, requestCode, intent, flags)
|
||||||
i
|
}
|
||||||
|
|
||||||
|
fun getWidgetUpdateIntent(context: Context): Intent {
|
||||||
|
val widgetManager = AppWidgetManager.getInstance(context)
|
||||||
|
val widgetComponent = ComponentName(context, MainWidget::class.java)
|
||||||
|
val widgetIds = widgetManager.getAppWidgetIds(widgetComponent)
|
||||||
|
return Intent(context, MainWidget::class.java).apply {
|
||||||
|
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds)
|
||||||
|
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCalendarIntent(context: Context): Intent {
|
private fun getWidgetRefreshIntent(context: Context): Intent {
|
||||||
return when (Preferences.calendarAppPackage) {
|
return Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
"" -> {
|
action = Actions.ACTION_REFRESH
|
||||||
Intent(Intent.ACTION_MAIN).apply {
|
}
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
}
|
||||||
addCategory(Intent.CATEGORY_APP_CALENDAR)
|
|
||||||
}
|
fun getGoogleMapsIntentFromAddress(context: Context, address: String): Intent {
|
||||||
}
|
val gmmIntentUri: Uri = Uri.parse("geo:0,0?q=${Uri.encode(address)}")
|
||||||
"_" -> {
|
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
|
||||||
Intent()
|
//mapIntent.`package` = "com.google.android.apps.maps"
|
||||||
}
|
|
||||||
else -> {
|
return if (mapIntent.resolveActivity(context.packageManager) != null) {
|
||||||
val pm: PackageManager = context.packageManager
|
mapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
try {
|
} else {
|
||||||
pm.getLaunchIntentForPackage(Preferences.calendarAppPackage)!!.apply {
|
val map = "https://www.google.com/maps/search/?api=1&query=${Uri.encode(address)}"
|
||||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
Intent(Intent.ACTION_VIEW, Uri.parse(map)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
Intent(Intent.ACTION_MAIN).apply {
|
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
addCategory(Intent.CATEGORY_APP_CALENDAR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getWeatherIntent(context: Context): Intent {
|
fun getWeatherIntent(context: Context): Intent {
|
||||||
return when (Preferences.weatherAppPackage) {
|
return when (Preferences.weatherAppPackage) {
|
||||||
"" -> {
|
DEFAULT_OPTION -> {
|
||||||
Intent(Intent.ACTION_VIEW).apply {
|
Intent(Intent.ACTION_VIEW).apply {
|
||||||
addCategory(Intent.CATEGORY_DEFAULT)
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
data = Uri.parse("dynact://velour/weather/ProxyActivity")
|
data = Uri.parse("dynact://velour/weather/ProxyActivity")
|
||||||
component = ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
|
component = ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
|
||||||
|
setClassName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"_" -> {
|
DO_NOTHING_OPTION -> {
|
||||||
Intent()
|
Intent()
|
||||||
}
|
}
|
||||||
|
REFRESH_WIDGET_OPTION -> {
|
||||||
|
getWidgetRefreshIntent(context)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val pm: PackageManager = context.packageManager
|
val pm: PackageManager = context.packageManager
|
||||||
try {
|
try {
|
||||||
@ -80,65 +88,121 @@ object IntentHelper {
|
|||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Intent(Intent.ACTION_VIEW).apply {
|
Intent()
|
||||||
addCategory(Intent.CATEGORY_DEFAULT)
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
data = Uri.parse("dynact://velour/weather/ProxyActivity")
|
|
||||||
component = ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEventIntent(context: Context, e: Event): Intent {
|
fun getCalendarIntent(context: Context, time: Long? = null): Intent {
|
||||||
return when (Preferences.eventAppPackage) {
|
val calendarUri = CalendarContract.CONTENT_URI
|
||||||
"" -> {
|
.buildUpon()
|
||||||
val uri = ContentUris.withAppendedId(Events.CONTENT_URI, e.eventID)
|
.appendPath("time")
|
||||||
|
.appendPath((time ?: Calendar.getInstance().timeInMillis).toString())
|
||||||
|
.build()
|
||||||
|
return when (Preferences.calendarAppPackage) {
|
||||||
|
DEFAULT_OPTION -> {
|
||||||
Intent(Intent.ACTION_VIEW).apply {
|
Intent(Intent.ACTION_VIEW).apply {
|
||||||
data = uri
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
data = calendarUri
|
||||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
|
||||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"_" -> {
|
DO_NOTHING_OPTION -> {
|
||||||
Intent()
|
Intent()
|
||||||
}
|
}
|
||||||
|
REFRESH_WIDGET_OPTION -> {
|
||||||
|
getWidgetRefreshIntent(context)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val pm: PackageManager = context.packageManager
|
val pm: PackageManager = context.packageManager
|
||||||
val uri = ContentUris.withAppendedId(Events.CONTENT_URI, e.eventID)
|
|
||||||
try {
|
try {
|
||||||
pm.getLaunchIntentForPackage(Preferences.eventAppPackage)!!.apply {
|
pm.getLaunchIntentForPackage(Preferences.calendarAppPackage)!!.apply {
|
||||||
action = Intent.ACTION_VIEW
|
|
||||||
data = uri
|
|
||||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
// putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
action = Intent.ACTION_VIEW
|
||||||
// putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
data = calendarUri
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Intent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEventIntent(context: Context, e: Event, forceEventDetails: Boolean = false): Intent {
|
||||||
|
return when (Preferences.openEventDetails || forceEventDetails) {
|
||||||
|
true -> {
|
||||||
|
val uri = ContentUris.withAppendedId(Events.CONTENT_URI, e.eventID)
|
||||||
|
if (Preferences.calendarAppPackage == "") {
|
||||||
Intent(Intent.ACTION_VIEW).apply {
|
Intent(Intent.ACTION_VIEW).apply {
|
||||||
data = uri
|
data = uri
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
flags = (Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
if (!e.allDay) {
|
||||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
||||||
|
} else {
|
||||||
|
val start = Calendar.getInstance().apply {
|
||||||
|
timeInMillis = e.startDate
|
||||||
|
}
|
||||||
|
val end = Calendar.getInstance().apply {
|
||||||
|
timeInMillis = e.endDate
|
||||||
|
}
|
||||||
|
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate + start.timeZone.getOffset(start.timeInMillis))
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate + end.timeZone.getOffset(end.timeInMillis))
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val calendarIntent = getCalendarIntent(context)
|
||||||
|
if (calendarIntent.action == Intent.ACTION_VIEW) {
|
||||||
|
calendarIntent.apply {
|
||||||
|
data = uri
|
||||||
|
if (!e.allDay) {
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
||||||
|
} else {
|
||||||
|
val start = Calendar.getInstance().apply {
|
||||||
|
timeInMillis = e.startDate
|
||||||
|
}
|
||||||
|
val end = Calendar.getInstance().apply {
|
||||||
|
timeInMillis = e.endDate
|
||||||
|
}
|
||||||
|
putExtra(
|
||||||
|
CalendarContract.EXTRA_EVENT_BEGIN_TIME,
|
||||||
|
start.timeInMillis + start.timeZone.getOffset(start.timeInMillis)
|
||||||
|
)
|
||||||
|
putExtra(
|
||||||
|
CalendarContract.EXTRA_EVENT_END_TIME,
|
||||||
|
end.timeInMillis + end.timeZone.getOffset(end.timeInMillis)
|
||||||
|
)
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Intent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
false -> {
|
||||||
|
getCalendarIntent(context, e.startDate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getClockIntent(context: Context): Intent {
|
fun getClockIntent(context: Context): Intent {
|
||||||
return when (Preferences.clockAppPackage) {
|
return when (Preferences.clockAppPackage) {
|
||||||
"" -> {
|
DEFAULT_OPTION -> {
|
||||||
Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"_" -> {
|
DO_NOTHING_OPTION -> {
|
||||||
Intent()
|
Intent()
|
||||||
}
|
}
|
||||||
|
REFRESH_WIDGET_OPTION -> {
|
||||||
|
getWidgetRefreshIntent(context)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val pm: PackageManager = context.packageManager
|
val pm: PackageManager = context.packageManager
|
||||||
try {
|
try {
|
||||||
@ -146,11 +210,56 @@ object IntentHelper {
|
|||||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
Intent()
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getBatteryIntent(): Intent {
|
||||||
|
return Intent(Intent.ACTION_POWER_USAGE_SUMMARY).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMusicIntent(context: Context): Intent {
|
||||||
|
return when (Preferences.mediaPlayerPackage) {
|
||||||
|
DO_NOTHING_OPTION -> {
|
||||||
|
Intent()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val pm: PackageManager = context.packageManager
|
||||||
|
try {
|
||||||
|
pm.getLaunchIntentForPackage(Preferences.mediaPlayerPackage)!!.apply {
|
||||||
|
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Intent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFitIntent(context: Context): Intent {
|
||||||
|
val pm: PackageManager = context.packageManager
|
||||||
|
return try {
|
||||||
|
pm.getLaunchIntentForPackage("com.google.android.apps.fitness")!!.apply {
|
||||||
|
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Intent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNotificationIntent(context: Context): Intent {
|
||||||
|
val pm: PackageManager = context.packageManager
|
||||||
|
return try {
|
||||||
|
pm.getLaunchIntentForPackage(Preferences.lastNotificationPackage)!!.apply {
|
||||||
|
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Intent()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.media.MediaMetadata
|
||||||
|
import android.media.session.MediaController
|
||||||
|
import android.media.session.MediaSessionManager
|
||||||
|
import android.media.session.PlaybackState
|
||||||
|
import com.chibatching.kotpref.Kotpref
|
||||||
|
import com.chibatching.kotpref.blockingBulk
|
||||||
|
import com.chibatching.kotpref.bulk
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.receivers.NotificationListener
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.ignoreExceptions
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
object MediaPlayerHelper {
|
||||||
|
const val MEDIA_INFO_TITLE = "%TITLE"
|
||||||
|
const val MEDIA_INFO_ARTIST = "%ARTIST"
|
||||||
|
const val MEDIA_INFO_ALBUM = "%ALBUM"
|
||||||
|
|
||||||
|
const val DEFAULT_MEDIA_INFO_FORMAT = "%TITLE, %ARTIST"
|
||||||
|
|
||||||
|
fun isSomeonePlaying(context: Context) = Preferences.showMusic && ActiveNotificationsHelper.checkNotificationAccess(context) && Preferences.mediaPlayerTitle != ""
|
||||||
|
|
||||||
|
fun getMediaInfo(format: String = Preferences.mediaInfoFormat, title: String = Preferences.mediaPlayerTitle, artist: String = Preferences.mediaPlayerArtist, album: String = Preferences.mediaPlayerAlbum): String {
|
||||||
|
return when (format) {
|
||||||
|
"",
|
||||||
|
DEFAULT_MEDIA_INFO_FORMAT -> {
|
||||||
|
if (Preferences.mediaPlayerArtist == "") {
|
||||||
|
Preferences.mediaPlayerTitle
|
||||||
|
} else {
|
||||||
|
DEFAULT_MEDIA_INFO_FORMAT.replace(MEDIA_INFO_TITLE, title)
|
||||||
|
.replace(MEDIA_INFO_ARTIST, artist)
|
||||||
|
.replace(MEDIA_INFO_ALBUM, album)
|
||||||
|
.replace("\\n", System.getProperty("line.separator") ?: " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
format.replace(MEDIA_INFO_TITLE, title)
|
||||||
|
.replace(MEDIA_INFO_ARTIST, artist)
|
||||||
|
.replace(MEDIA_INFO_ALBUM, album)
|
||||||
|
.replace("\\n", System.getProperty("line.separator") ?: " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updatePlayingMediaInfo(context: Context) {
|
||||||
|
Kotpref.init(context)
|
||||||
|
if (ActiveNotificationsHelper.checkNotificationAccess(context)) {
|
||||||
|
val list = try {
|
||||||
|
(context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager).getActiveSessions(
|
||||||
|
ComponentName(context.packageName, NotificationListener::class.java.name)
|
||||||
|
)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
emptyList<MediaController>()
|
||||||
|
}.filter {
|
||||||
|
Preferences.musicPlayersFilter == "" || isMusicPlayerAccepted(it.packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
var isSomeonePlaying = false
|
||||||
|
list.forEach { mc ->
|
||||||
|
val metadata = mc.metadata
|
||||||
|
val isPlaying =
|
||||||
|
mc.playbackState?.state == PlaybackState.STATE_PLAYING || mc.playbackState?.state == PlaybackState.STATE_CONNECTING
|
||||||
|
|
||||||
|
if (isPlaying) {
|
||||||
|
isSomeonePlaying = true
|
||||||
|
if (metadata != null) {
|
||||||
|
Preferences.bulk {
|
||||||
|
ignoreExceptions {
|
||||||
|
mediaPlayerTitle =
|
||||||
|
metadata.getText(MediaMetadata.METADATA_KEY_TITLE)
|
||||||
|
?.toString()
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
ignoreExceptions {
|
||||||
|
mediaPlayerArtist =
|
||||||
|
metadata.getText(MediaMetadata.METADATA_KEY_ARTIST)
|
||||||
|
?.toString()
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
ignoreExceptions {
|
||||||
|
mediaPlayerAlbum =
|
||||||
|
metadata.getText(MediaMetadata.METADATA_KEY_ALBUM)
|
||||||
|
?.toString()
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences.mediaPlayerPackage = mc.packageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSomeonePlaying) {
|
||||||
|
removeMediaInfo(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
removeMediaInfo(context)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removeMediaInfo(context)
|
||||||
|
}
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeMediaInfo(context: Context) {
|
||||||
|
Kotpref.init(context)
|
||||||
|
Preferences.blockingBulk {
|
||||||
|
remove(Preferences::mediaPlayerTitle)
|
||||||
|
remove(Preferences::mediaPlayerArtist)
|
||||||
|
remove(Preferences::mediaPlayerAlbum)
|
||||||
|
remove(Preferences::mediaPlayerPackage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isMusicPlayerAccepted(appPkg: String): Boolean = Preferences.musicPlayersFilter == "" || Preferences.musicPlayersFilter.contains(appPkg)
|
||||||
|
|
||||||
|
fun toggleMusicPlayerFilter(appPkg: String) {
|
||||||
|
if (Preferences.musicPlayersFilter == "" || !Preferences.musicPlayersFilter.contains(appPkg)) {
|
||||||
|
Preferences.musicPlayersFilter = Preferences.musicPlayersFilter.split(",").union(listOf(appPkg)).joinToString(separator = ",")
|
||||||
|
} else {
|
||||||
|
Preferences.musicPlayersFilter = Preferences.musicPlayersFilter.split(",").filter { it != appPkg }.joinToString(separator = ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ package com.tommasoberlose.anotherwidget.helpers
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@ -39,7 +41,6 @@ object SettingsStringHelper {
|
|||||||
return when (info) {
|
return when (info) {
|
||||||
0 -> R.string.settings_second_row_info_subtitle_0
|
0 -> R.string.settings_second_row_info_subtitle_0
|
||||||
1 -> R.string.settings_second_row_info_subtitle_1
|
1 -> R.string.settings_second_row_info_subtitle_1
|
||||||
2 -> R.string.settings_second_row_info_subtitle_2
|
|
||||||
else -> R.string.settings_second_row_info_subtitle_0
|
else -> R.string.settings_second_row_info_subtitle_0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,27 +54,60 @@ object SettingsStringHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCustomFontLabel(shadow: Int): Int {
|
fun getCustomFontLabel(context: Context, font: Int): String {
|
||||||
return when (shadow) {
|
return when (font) {
|
||||||
0 -> R.string.custom_font_subtitle_0
|
Constants.CUSTOM_FONT_GOOGLE_SANS -> context.getString(R.string.custom_font_subtitle_1) + " - ${getVariantLabel(context, Preferences.customFontVariant)}"
|
||||||
1 -> R.string.custom_font_subtitle_1
|
Constants.CUSTOM_FONT_DOWNLOADED -> Preferences.customFontName + " - ${getVariantLabel(context, Preferences.customFontVariant)}"
|
||||||
else -> R.string.custom_font_subtitle_1
|
else -> context.getString(R.string.custom_font_subtitle_0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getVariantLabel(context: Context, variant: String): String = when {
|
||||||
|
variant == "italic" -> context.getString(R.string.font_italic)
|
||||||
|
variant.contains("100") && variant.contains("italic") -> context.getString(R.string.font_100_italic)
|
||||||
|
variant.contains("200") && variant.contains("italic") -> context.getString(R.string.font_200_italic)
|
||||||
|
variant.contains("300") && variant.contains("italic") -> context.getString(R.string.font_300_italic)
|
||||||
|
variant.contains("400") && variant.contains("italic") -> context.getString(R.string.font_400_italic)
|
||||||
|
variant.contains("500") && variant.contains("italic") -> context.getString(R.string.font_500_italic)
|
||||||
|
variant.contains("600") && variant.contains("italic") -> context.getString(R.string.font_600_italic)
|
||||||
|
variant.contains("700") && variant.contains("italic") -> context.getString(R.string.font_700_italic)
|
||||||
|
variant.contains("800") && variant.contains("italic") -> context.getString(R.string.font_800_italic)
|
||||||
|
variant.contains("900") && variant.contains("italic") -> context.getString(R.string.font_900_italic)
|
||||||
|
variant == "regular" || variant.contains("400") -> context.getString(R.string.font_400)
|
||||||
|
variant.contains("100") -> context.getString(R.string.font_100)
|
||||||
|
variant.contains("200") -> context.getString(R.string.font_200)
|
||||||
|
variant.contains("300") -> context.getString(R.string.font_300)
|
||||||
|
variant.contains("500") -> context.getString(R.string.font_500)
|
||||||
|
variant.contains("600") -> context.getString(R.string.font_600)
|
||||||
|
variant.contains("700") -> context.getString(R.string.font_700)
|
||||||
|
variant.contains("800") -> context.getString(R.string.font_800)
|
||||||
|
variant.contains("900") -> context.getString(R.string.font_900)
|
||||||
|
else -> context.getString(R.string.font_400)
|
||||||
|
}
|
||||||
|
|
||||||
fun getDifferenceText(context: Context, now: Long, start: Long): String {
|
fun getDifferenceText(context: Context, now: Long, start: Long): String {
|
||||||
val nowDate = DateTime(now)
|
val nowDate = DateTime(now)
|
||||||
val eventDate = DateTime(start)
|
val eventDate = DateTime(start)
|
||||||
|
val difference = start - now
|
||||||
var difference = start - now
|
|
||||||
difference += 60 * 1000 - (difference % (60 * 1000))
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
difference <= 0 || TimeUnit.MILLISECONDS.toHours(difference) < 1 -> {
|
difference <= 0 -> {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) >= 5 -> {
|
||||||
|
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - TimeUnit.MILLISECONDS.toMinutes(difference) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||||
|
}
|
||||||
|
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) >= 15 -> {
|
||||||
|
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - TimeUnit.MILLISECONDS.toMinutes(difference) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||||
|
}
|
||||||
|
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.LOW.rawValue -> {
|
||||||
|
return context.getString(R.string.soon)
|
||||||
|
}
|
||||||
|
TimeUnit.MILLISECONDS.toHours(difference) < 1 -> {
|
||||||
|
return context.getString(R.string.now)
|
||||||
|
}
|
||||||
TimeUnit.MILLISECONDS.toHours(difference) < 12 -> {
|
TimeUnit.MILLISECONDS.toHours(difference) < 12 -> {
|
||||||
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS).toString()
|
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||||
}
|
}
|
||||||
eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> {
|
eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> {
|
||||||
return String.format("%s", context.getString(R.string.tomorrow))
|
return String.format("%s", context.getString(R.string.tomorrow))
|
||||||
@ -82,7 +116,7 @@ object SettingsStringHelper {
|
|||||||
return String.format("%s", context.getString(R.string.today))
|
return String.format("%s", context.getString(R.string.today))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.DAY_IN_MILLIS).toString()
|
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +126,6 @@ object SettingsStringHelper {
|
|||||||
val nowDate = DateTime(now)
|
val nowDate = DateTime(now)
|
||||||
val eventDate = DateTime(start)
|
val eventDate = DateTime(start)
|
||||||
|
|
||||||
var difference = start - now
|
|
||||||
difference += 60 * 1000 - (difference % (60 * 1000))
|
|
||||||
|
|
||||||
return when (eventDate.dayOfYear) {
|
return when (eventDate.dayOfYear) {
|
||||||
nowDate.dayOfYear -> {
|
nowDate.dayOfYear -> {
|
||||||
""
|
""
|
||||||
|
@ -2,16 +2,15 @@ package com.tommasoberlose.anotherwidget.helpers
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.google.android.gms.location.LocationServices
|
import com.chibatching.kotpref.Kotpref
|
||||||
import com.kwabenaberko.openweathermaplib.constants.Units
|
|
||||||
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
|
|
||||||
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
|
|
||||||
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||||
|
import com.tommasoberlose.anotherwidget.services.WeatherWorker
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,111 +19,488 @@ import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
|||||||
|
|
||||||
object WeatherHelper {
|
object WeatherHelper {
|
||||||
|
|
||||||
fun updateWeather(context: Context) {
|
fun updateWeather(context: Context, force: Boolean = false) {
|
||||||
val networkApi = WeatherNetworkApi(context)
|
if (Preferences.showWeather || force)
|
||||||
if (Preferences.customLocationAdd != "") {
|
WeatherWorker.enqueue(context, replace = force)
|
||||||
networkApi.updateWeather()
|
else {
|
||||||
} else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
removeWeather(context)
|
||||||
LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener { task ->
|
org.greenrobot.eventbus.EventBus.getDefault().post(
|
||||||
if (task.isSuccessful) {
|
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
|
||||||
val location = task.result
|
)
|
||||||
if (location != null) {
|
|
||||||
Preferences.customLocationLat = location.latitude.toString()
|
|
||||||
Preferences.customLocationLon = location.longitude.toString()
|
|
||||||
|
|
||||||
networkApi.updateWeather()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeWeather(context: Context) {
|
fun removeWeather(context: Context) {
|
||||||
Preferences.remove(Preferences::weatherTemp)
|
Preferences.remove(Preferences::weatherTemp)
|
||||||
Preferences.remove(Preferences::weatherTempUnit)
|
Preferences.remove(Preferences::weatherRealTempUnit)
|
||||||
|
Preferences.remove(Preferences::weatherIcon)
|
||||||
MainWidget.updateWidget(context)
|
MainWidget.updateWidget(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getWeatherIconResource(icon: String): Int {
|
fun getProviderName(context: Context, provider: Constants.WeatherProvider = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||||
when (icon) {
|
return context.getString(when(provider) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> R.string.settings_weather_provider_open_weather
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> R.string.settings_weather_provider_weatherbit
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> R.string.settings_weather_provider_weather_api
|
||||||
|
Constants.WeatherProvider.HERE -> R.string.settings_weather_provider_here
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> R.string.settings_weather_provider_accuweather
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV -> R.string.settings_weather_provider_weather_gov
|
||||||
|
Constants.WeatherProvider.YR -> R.string.settings_weather_provider_yr
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProviderInfoTitle(context: Context, provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||||
|
return context.getString(when(provider) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> R.string.weather_provider_info_open_weather_title
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> R.string.weather_provider_info_weatherbit_title
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> R.string.weather_provider_info_weatherapi_title
|
||||||
|
Constants.WeatherProvider.HERE -> R.string.weather_provider_info_here_title
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> R.string.weather_provider_info_accuweather_title
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV -> R.string.weather_provider_info_weather_gov_title
|
||||||
|
Constants.WeatherProvider.YR -> R.string.weather_provider_info_yr_title
|
||||||
|
else -> R.string.nothing
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProviderInfoSubtitle(context: Context, provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||||
|
return context.getString(when(provider) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> R.string.weather_provider_info_open_weather_subtitle
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> R.string.weather_provider_info_weatherbit_subtitle
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> R.string.weather_provider_info_weatherapi_subtitle
|
||||||
|
Constants.WeatherProvider.HERE -> R.string.weather_provider_info_here_subtitle
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> R.string.weather_provider_info_accuweather_subtitle
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV -> R.string.weather_provider_info_weather_gov_subtitle
|
||||||
|
Constants.WeatherProvider.YR -> R.string.weather_provider_info_yr_subtitle
|
||||||
|
else -> R.string.nothing
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProviderLink(provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||||
|
return when(provider) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> "https://home.openweathermap.org/users/sign_in"
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> "https://www.weatherbit.io/account/login"
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> "https://www.weatherapi.com/login.aspx"
|
||||||
|
Constants.WeatherProvider.HERE -> "https://developer.here.com/login"
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> "https://developer.accuweather.com/user/login"
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV -> "http://www.weather.gov/"
|
||||||
|
Constants.WeatherProvider.YR -> "https://www.yr.no/"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isKeyRequired(provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): Boolean = when (provider) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER,
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT,
|
||||||
|
Constants.WeatherProvider.WEATHER_API,
|
||||||
|
Constants.WeatherProvider.HERE,
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> true
|
||||||
|
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV,
|
||||||
|
Constants.WeatherProvider.YR -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getApiKey(provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String = when (provider) {
|
||||||
|
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen
|
||||||
|
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit
|
||||||
|
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi
|
||||||
|
Constants.WeatherProvider.HERE -> Preferences.weatherProviderApiHere
|
||||||
|
Constants.WeatherProvider.ACCUWEATHER -> Preferences.weatherProviderApiAccuweather
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV -> ""
|
||||||
|
Constants.WeatherProvider.YR -> ""
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWeatherIconResource(context: Context, icon: String, style: Int = Preferences.weatherIconPack): Int {
|
||||||
|
return when (icon) {
|
||||||
"01d" -> {
|
"01d" -> {
|
||||||
return R.drawable.clear_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.clear_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.clear_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.clear_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.clear_day_5 else R.drawable.clear_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"02d" -> {
|
"02d" -> {
|
||||||
return R.drawable.partly_cloudy
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.partly_cloudy_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.partly_cloudy_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.partly_cloudy_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.partly_cloudy_5 else R.drawable.partly_cloudy_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"03d" -> {
|
"03d" -> {
|
||||||
return R.drawable.mostly_cloudy
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.mostly_cloudy_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.mostly_cloudy_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.mostly_cloudy_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.mostly_cloudy_5 else R.drawable.mostly_cloudy_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"04d" -> {
|
"04d" -> {
|
||||||
return R.drawable.cloudy_weather
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.cloudy_weather_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.cloudy_weather_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.cloudy_weather_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.cloudy_weather_5 else R.drawable.cloudy_weather_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"09d" -> {
|
"09d" -> {
|
||||||
return R.drawable.storm_weather_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.storm_weather_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.storm_weather_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.storm_weather_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.storm_weather_day_5 else R.drawable.storm_weather_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"10d" -> {
|
"10d" -> {
|
||||||
return R.drawable.rainy_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.rainy_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.rainy_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.rainy_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.rainy_day_5 else R.drawable.rainy_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"11d" -> {
|
"11d" -> {
|
||||||
return R.drawable.thunder_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.thunder_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.thunder_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.thunder_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.thunder_day_5 else R.drawable.thunder_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"13d" -> {
|
"13d" -> {
|
||||||
return R.drawable.snow_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.snow_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.snow_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.snow_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.snow_day_5 else R.drawable.snow_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"50d" -> {
|
"50d" -> {
|
||||||
return R.drawable.haze_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.haze_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.haze_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.haze_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.haze_day_5 else R.drawable.haze_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"80d" -> {
|
"80d" -> {
|
||||||
return R.drawable.windy_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.windy_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.windy_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.windy_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.windy_day_5 else R.drawable.windy_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"81d" -> {
|
"81d" -> {
|
||||||
return R.drawable.rain_snow_day
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.rain_snow_day_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.rain_snow_day_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.rain_snow_day_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.rain_snow_day_5 else R.drawable.rain_snow_day_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"82d" -> {
|
"82d" -> {
|
||||||
return R.drawable.haze_weather
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.haze_weather_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.haze_weather_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.haze_weather_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.haze_weather_5 else R.drawable.haze_weather_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"01n" -> {
|
"01n" -> {
|
||||||
return R.drawable.clear_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.clear_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.clear_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.clear_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.clear_night_5 else R.drawable.clear_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"02n" -> {
|
"02n" -> {
|
||||||
return R.drawable.partly_cloudy_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.partly_cloudy_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.partly_cloudy_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.partly_cloudy_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.partly_cloudy_night_5 else R.drawable.partly_cloudy_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"03n" -> {
|
"03n" -> {
|
||||||
return R.drawable.mostly_cloudy_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.mostly_cloudy_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.mostly_cloudy_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.mostly_cloudy_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.mostly_cloudy_night_5 else R.drawable.mostly_cloudy_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"04n" -> {
|
"04n" -> {
|
||||||
return R.drawable.cloudy_weather
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.cloudy_weather_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.cloudy_weather_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.cloudy_weather_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.cloudy_weather_5 else R.drawable.cloudy_weather_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"09n" -> {
|
"09n" -> {
|
||||||
return R.drawable.storm_weather_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.storm_weather_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.storm_weather_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.storm_weather_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.storm_weather_night_5 else R.drawable.storm_weather_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"10n" -> {
|
"10n" -> {
|
||||||
return R.drawable.rainy_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.rainy_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.rainy_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.rainy_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.rainy_night_5 else R.drawable.rainy_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"11n" -> {
|
"11n" -> {
|
||||||
return R.drawable.thunder_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.thunder_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.thunder_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.thunder_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.thunder_night_5 else R.drawable.thunder_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"13n" -> {
|
"13n" -> {
|
||||||
return R.drawable.snow_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.snow_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.snow_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.snow_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.snow_night_5 else R.drawable.snow_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"50n" -> {
|
"50n" -> {
|
||||||
return R.drawable.haze_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.haze_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.haze_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.haze_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.haze_night_5 else R.drawable.haze_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"80n" -> {
|
"80n" -> {
|
||||||
return R.drawable.windy_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.windy_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.windy_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.windy_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.windy_night_5 else R.drawable.windy_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"81n" -> {
|
"81n" -> {
|
||||||
return R.drawable.rain_snow_night
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.rain_snow_night_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.rain_snow_night_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.rain_snow_night_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.rain_snow_night_5 else R.drawable.rain_snow_night_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"82n" -> {
|
"82n" -> {
|
||||||
return R.drawable.haze_weather
|
when (style) {
|
||||||
|
Constants.WeatherIconPack.COOL.rawValue -> R.drawable.haze_weather_3
|
||||||
|
Constants.WeatherIconPack.MINIMAL.rawValue -> R.drawable.haze_weather_2
|
||||||
|
Constants.WeatherIconPack.GOOGLE_NEWS.rawValue -> R.drawable.haze_weather_4
|
||||||
|
else -> if (context.isDarkTheme()) R.drawable.haze_weather_5 else R.drawable.haze_weather_5_light
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
return R.drawable.unknown
|
return if (context.isDarkTheme()) R.drawable.unknown_dark else R.drawable.unknown_light
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fun getWeatherLabel(context: Context, icon: String): String {
|
||||||
|
return when (icon) {
|
||||||
|
"01d", "01n" -> context.getString(R.string.weather_label_clear)
|
||||||
|
"02d", "02n" -> context.getString(R.string.weather_label_partly_cloudy)
|
||||||
|
"03d", "03n" -> context.getString(R.string.weather_label_mostly_cloudy)
|
||||||
|
"04d", "04n" -> context.getString(R.string.weather_label_cloudy_weather)
|
||||||
|
"09d", "09n" -> context.getString(R.string.weather_label_storm_weather)
|
||||||
|
"10d", "10n" -> context.getString(R.string.weather_label_rainy)
|
||||||
|
"11d", "11n" -> context.getString(R.string.weather_label_thunder)
|
||||||
|
"13d", "13n" -> context.getString(R.string.weather_label_snow)
|
||||||
|
"50d", "50n", "82d", "82n" -> context.getString(R.string.weather_label_haze)
|
||||||
|
"80d", "80n" -> context.getString(R.string.weather_label_windy)
|
||||||
|
"81d", "81n" -> context.getString(R.string.weather_label_rain_snow)
|
||||||
|
else -> context.getString(R.string.weather_label_unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when (
|
||||||
|
iconString.substringBefore('?').substringAfterLast('/').substringBefore(',')
|
||||||
|
) {
|
||||||
|
"skc" -> "01"
|
||||||
|
"few" -> "02"
|
||||||
|
"sct" -> "02"
|
||||||
|
"bkn" -> "03"
|
||||||
|
"ovc" -> "04"
|
||||||
|
"wind_skc" -> "01"
|
||||||
|
"wind_few" -> "02"
|
||||||
|
"wind_sct" -> "02"
|
||||||
|
"wind_bkn" -> "03"
|
||||||
|
"wind_ovc" -> "04"
|
||||||
|
"snow" -> "13"
|
||||||
|
"rain_snow" -> "81"
|
||||||
|
"rain_sleet" -> "81"
|
||||||
|
"snow_sleet" -> "81"
|
||||||
|
"fzra" -> "81"
|
||||||
|
"rain_fzra" -> "81"
|
||||||
|
"snow_fzra" -> "81"
|
||||||
|
"sleet" -> "81"
|
||||||
|
"rain" -> "10"
|
||||||
|
"rain_showers" -> "10"
|
||||||
|
"rain_showers_hi" -> "10"
|
||||||
|
"tsra" -> "09"
|
||||||
|
"tsra_sct" -> "11"
|
||||||
|
"tsra_hi" -> "11"
|
||||||
|
"tornado" -> "80"
|
||||||
|
"hurricane" -> "80"
|
||||||
|
"tropical_storm" -> "09"
|
||||||
|
"dust" -> "50"
|
||||||
|
"smoke" -> "50"
|
||||||
|
"haze" -> "50"
|
||||||
|
"hot" -> "01"
|
||||||
|
"cold" -> "13"
|
||||||
|
"blizzard" -> "13"
|
||||||
|
"fog" -> "82"
|
||||||
|
else -> ""
|
||||||
|
} + if (isDaytime) "d" else "n"
|
||||||
|
|
||||||
|
fun getWeatherBitIcon(iconString: String): String = when (iconString.substring(0, 3)) {
|
||||||
|
"t01" -> "11"
|
||||||
|
"t02" -> "11"
|
||||||
|
"t03" -> "09"
|
||||||
|
"t04" -> "11"
|
||||||
|
"t05" -> "11"
|
||||||
|
"d01" -> "10"
|
||||||
|
"d02" -> "10"
|
||||||
|
"d03" -> "10"
|
||||||
|
"r01" -> "10"
|
||||||
|
"r02" -> "10"
|
||||||
|
"r03" -> "10"
|
||||||
|
"f01" -> "10"
|
||||||
|
"r04" -> "10"
|
||||||
|
"r05" -> "10"
|
||||||
|
"r06" -> "10"
|
||||||
|
"s01" -> "13"
|
||||||
|
"s02" -> "13"
|
||||||
|
"s03" -> "13"
|
||||||
|
"s04" -> "81"
|
||||||
|
"s05" -> "81"
|
||||||
|
"s06" -> "13"
|
||||||
|
"a01" -> "50"
|
||||||
|
"a02" -> "50"
|
||||||
|
"a03" -> "50"
|
||||||
|
"a04" -> "50"
|
||||||
|
"a05" -> "82"
|
||||||
|
"a06" -> "82"
|
||||||
|
"c01" -> "01"
|
||||||
|
"c02" -> "02"
|
||||||
|
"c03" -> "03"
|
||||||
|
"c04" -> "04"
|
||||||
|
else -> ""
|
||||||
|
} + iconString.substring(3)
|
||||||
|
|
||||||
|
fun getWeatherApiIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
|
||||||
|
1000 -> "01"
|
||||||
|
1003 -> "02"
|
||||||
|
1006 -> "03"
|
||||||
|
1009 -> "04"
|
||||||
|
1030 -> "50"
|
||||||
|
1063 -> "10"
|
||||||
|
1066 -> "13"
|
||||||
|
1069 -> "81"
|
||||||
|
1072 -> "81"
|
||||||
|
1087 -> "11"
|
||||||
|
1114 -> "13"
|
||||||
|
1117 -> "13"
|
||||||
|
1135 -> "82"
|
||||||
|
1147 -> "82"
|
||||||
|
1150 -> "10"
|
||||||
|
1153 -> "10"
|
||||||
|
1168 -> "10"
|
||||||
|
1171 -> "10"
|
||||||
|
1180 -> "10"
|
||||||
|
1183 -> "10"
|
||||||
|
1186 -> "10"
|
||||||
|
1189 -> "10"
|
||||||
|
1192 -> "10"
|
||||||
|
1195 -> "10"
|
||||||
|
1198 -> "81"
|
||||||
|
1201 -> "81"
|
||||||
|
1204 -> "81"
|
||||||
|
1207 -> "81"
|
||||||
|
1210 -> "13"
|
||||||
|
1213 -> "13"
|
||||||
|
1216 -> "13"
|
||||||
|
1219 -> "13"
|
||||||
|
1222 -> "13"
|
||||||
|
1225 -> "13"
|
||||||
|
1237 -> "13"
|
||||||
|
1240 -> "10"
|
||||||
|
1243 -> "10"
|
||||||
|
1246 -> "10"
|
||||||
|
1249 -> "81"
|
||||||
|
1252 -> "81"
|
||||||
|
1255 -> "13"
|
||||||
|
1258 -> "13"
|
||||||
|
1261 -> "13"
|
||||||
|
1264 -> "13"
|
||||||
|
1273 -> "11"
|
||||||
|
1276 -> "09"
|
||||||
|
1279 -> "13"
|
||||||
|
1282 -> "13"
|
||||||
|
else -> ""
|
||||||
|
} + if (isDaytime) "d" else "n"
|
||||||
|
|
||||||
|
fun getYRIcon(iconCode: String): String = when (iconCode.substringBefore('_')) {
|
||||||
|
"clearsky" -> "01"
|
||||||
|
"cloudy" -> "04"
|
||||||
|
"fair" -> "02"
|
||||||
|
"fog" -> "82"
|
||||||
|
"heavyrain" -> "10"
|
||||||
|
"heavyrainandthunder" -> "09"
|
||||||
|
"heavyrainshowers" -> "10"
|
||||||
|
"heavyrainshowersandthunder" -> "09"
|
||||||
|
"heavysleet" -> "81"
|
||||||
|
"heavysleetandthunder" -> "81"
|
||||||
|
"heavysleetshowers" -> "81"
|
||||||
|
"heavysleetshowersandthunder" -> "81"
|
||||||
|
"heavysnow" -> "13"
|
||||||
|
"heavysnowandthunder" -> "13"
|
||||||
|
"heavysnowshowers" -> "13"
|
||||||
|
"heavysnowshowersandthunder" -> "13"
|
||||||
|
"lightrain" -> "10"
|
||||||
|
"lightrainandthunder" -> "11"
|
||||||
|
"lightrainshowers" -> "10"
|
||||||
|
"lightrainshowersandthunder" -> "11"
|
||||||
|
"lightsleet" -> "81"
|
||||||
|
"lightsleetandthunder" -> "81"
|
||||||
|
"lightsleetshowers" -> "81"
|
||||||
|
"lightsnow" -> "13"
|
||||||
|
"lightsnowandthunder" -> "13"
|
||||||
|
"lightsnowshowers" -> "13"
|
||||||
|
"lightssleetshowersandthunder" -> "81"
|
||||||
|
"lightssnowshowersandthunder" -> "81"
|
||||||
|
"partlycloudy" -> "03"
|
||||||
|
"rain" -> "10"
|
||||||
|
"rainandthunder" -> "11"
|
||||||
|
"rainshowers" -> "10"
|
||||||
|
"rainshowersandthunder" -> "11"
|
||||||
|
"sleet" -> "81"
|
||||||
|
"sleetandthunder" -> "81"
|
||||||
|
"sleetshowers" -> "81"
|
||||||
|
"sleetshowersandthunder" -> "81"
|
||||||
|
"snow" -> "13"
|
||||||
|
"snowandthunder" -> "13"
|
||||||
|
"snowshowers" -> "13"
|
||||||
|
"snowshowersandthunder" -> "13"
|
||||||
|
else -> ""
|
||||||
|
} + if (iconCode.substringAfter('_', "day") == "day") "d" else "n"
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.helpers
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import androidx.core.provider.FontRequest
|
||||||
|
import androidx.core.provider.FontsContractCompat
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
|
||||||
|
object WidgetHelper {
|
||||||
|
class WidgetSizeProvider(
|
||||||
|
private val context: Context,
|
||||||
|
private val appWidgetManager: AppWidgetManager
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun getWidgetsSize(widgetId: Int): Pair<Int, Int> {
|
||||||
|
val portrait = context.resources.configuration.orientation == ORIENTATION_PORTRAIT
|
||||||
|
val width = getWidgetSizeInDp(
|
||||||
|
widgetId,
|
||||||
|
if (portrait) AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH else AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
|
||||||
|
)
|
||||||
|
val height = getWidgetSizeInDp(
|
||||||
|
widgetId,
|
||||||
|
if (portrait) AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT else AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
|
||||||
|
)
|
||||||
|
val widthInPx = context.dip(width)
|
||||||
|
val heightInPx = context.dip(height)
|
||||||
|
return widthInPx to heightInPx
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWidgetWidth(widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
|
||||||
|
|
||||||
|
private fun getWidgetHeight(widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
|
||||||
|
|
||||||
|
private fun getWidgetSizeInDp(widgetId: Int, key: String): Int =
|
||||||
|
appWidgetManager.getAppWidgetOptions(widgetId).getInt(key, 0)
|
||||||
|
|
||||||
|
private fun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Pair<Int, Int>.reduceDimensionWithMaxWidth(width: Int): Pair<Int, Int> {
|
||||||
|
return if (first < width) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
val factor = width / first
|
||||||
|
width to second * factor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runWithCustomTypeface(context: Context, function: (typeface: Typeface?) -> Unit) {
|
||||||
|
if (Preferences.customFontFile != "") {
|
||||||
|
val request = FontRequest(
|
||||||
|
"com.google.android.gms.fonts",
|
||||||
|
"com.google.android.gms",
|
||||||
|
Preferences.customFontFile,
|
||||||
|
R.array.com_google_android_gms_fonts_certs
|
||||||
|
)
|
||||||
|
|
||||||
|
val handlerThread = HandlerThread("generateView")
|
||||||
|
val callback = object : FontsContractCompat.FontRequestCallback() {
|
||||||
|
override fun onTypefaceRetrieved(typeface: Typeface) {
|
||||||
|
handlerThread.quit()
|
||||||
|
function.invoke(typeface)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTypefaceRequestFailed(reason: Int) {
|
||||||
|
handlerThread.quit()
|
||||||
|
function.invoke(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerThread.start()
|
||||||
|
//if (Looper.myLooper() == null) {
|
||||||
|
// Looper.prepare()
|
||||||
|
//}
|
||||||
|
|
||||||
|
Handler(handlerThread.looper).run {
|
||||||
|
FontsContractCompat.requestFont(context, request, callback, this)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
function.invoke(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,35 @@
|
|||||||
package com.tommasoberlose.anotherwidget.models
|
package com.tommasoberlose.anotherwidget.models
|
||||||
|
|
||||||
import io.realm.RealmObject
|
import android.provider.CalendarContract
|
||||||
import java.util.Date
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by tommaso on 05/10/17.
|
* Created by tommaso on 05/10/17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
open class Event(var id: Long = 0,
|
@Entity(tableName = "events")
|
||||||
var eventID: Long = 0,
|
data class Event(
|
||||||
var title: String = "",
|
@PrimaryKey
|
||||||
var startDate: Long = 0,
|
val id: Long = 0,
|
||||||
var endDate: Long = 0,
|
@ColumnInfo(name = "event_id")
|
||||||
var calendarID: Int = 0,
|
val eventID: Long = 0,
|
||||||
var allDay: Boolean = false,
|
val title: String = "",
|
||||||
var address: String = "") : RealmObject(){
|
@ColumnInfo(name = "start_date")
|
||||||
|
val startDate: Long = 0,
|
||||||
|
@ColumnInfo(name = "end_date")
|
||||||
|
val endDate: Long = 0,
|
||||||
|
@ColumnInfo(name = "calendar_id")
|
||||||
|
val calendarID: Long = 0,
|
||||||
|
@ColumnInfo(name = "all_day")
|
||||||
|
val allDay: Boolean = false,
|
||||||
|
val address: String = "",
|
||||||
|
@ColumnInfo(name = "self_attendee_status")
|
||||||
|
val selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
|
||||||
|
val availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
|
||||||
|
)/* {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Event:\nID: " + id + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL DAY: " + calendarID + "\nADDRESS: " + address
|
return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.models
|
||||||
|
|
||||||
|
class GlanceProvider(
|
||||||
|
val id: String,
|
||||||
|
val title: String,
|
||||||
|
val icon: Int
|
||||||
|
)
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.chibatching.kotpref.Kotpref
|
||||||
|
import com.google.gson.internal.LinkedTreeMap
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponse
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.network.repository.TimeZonesRepository
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class TimeZonesApi(val context: Context) {
|
||||||
|
suspend fun getTimeZone(lat: String, long: String): String? {
|
||||||
|
Kotpref.init(context)
|
||||||
|
val repository = TimeZonesRepository()
|
||||||
|
var id: String? = null
|
||||||
|
|
||||||
|
when (val response = repository.getTimeZone(lat, long)) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
id = response.body["timezoneId"] as String
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +1,531 @@
|
|||||||
package com.tommasoberlose.anotherwidget.network
|
package com.tommasoberlose.anotherwidget.network
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.chibatching.kotpref.Kotpref
|
||||||
|
import com.google.gson.internal.LinkedTreeMap
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponse
|
||||||
|
import com.haroldadmin.cnradapter.executeWithRetry
|
||||||
import com.kwabenaberko.openweathermaplib.constants.Units
|
import com.kwabenaberko.openweathermaplib.constants.Units
|
||||||
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
|
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
|
||||||
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
|
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
|
||||||
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
|
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
import com.tommasoberlose.anotherwidget.network.repository.*
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
class WeatherNetworkApi(val context: Context) {
|
class WeatherNetworkApi(val context: Context) {
|
||||||
fun updateWeather() {
|
suspend fun updateWeather() {
|
||||||
if (Preferences.showWeather && Preferences.weatherProviderApi != "" && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
|
Kotpref.init(context)
|
||||||
val helper = OpenWeatherMapHelper(Preferences.weatherProviderApi)
|
Preferences.weatherProviderError = "-"
|
||||||
helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC)
|
Preferences.weatherProviderLocationError = ""
|
||||||
helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object :
|
|
||||||
CurrentWeatherCallback {
|
|
||||||
override fun onSuccess(currentWeather: CurrentWeather?) {
|
|
||||||
currentWeather?.let {
|
|
||||||
Preferences.weatherTemp = currentWeather.main.temp.toFloat()
|
|
||||||
Preferences.weatherIcon = currentWeather.weather[0].icon
|
|
||||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
|
||||||
MainWidget.updateWidget(
|
|
||||||
context
|
|
||||||
)
|
|
||||||
|
|
||||||
EventBus.getDefault().post(MainActivity.UpdateUiMessageEvent())
|
if (Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
|
||||||
}
|
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||||
}
|
Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context)
|
||||||
|
Constants.WeatherProvider.WEATHER_GOV -> useWeatherGov(context)
|
||||||
override fun onFailure(throwable: Throwable?) {
|
Constants.WeatherProvider.WEATHER_BIT -> useWeatherBitProvider(context)
|
||||||
}
|
Constants.WeatherProvider.WEATHER_API -> useWeatherApiProvider(context)
|
||||||
|
Constants.WeatherProvider.HERE -> useHereProvider(context)
|
||||||
})
|
Constants.WeatherProvider.ACCUWEATHER -> useAccuweatherProvider(context)
|
||||||
|
Constants.WeatherProvider.YR -> useYrProvider(context)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
|
||||||
WeatherHelper.removeWeather(
|
WeatherHelper.removeWeather(
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private suspend fun useOpenWeatherMap(context: Context) {
|
||||||
|
if (Preferences.weatherProviderApiOpen != "") {
|
||||||
|
val helper = OpenWeatherMapHelper(Preferences.weatherProviderApiOpen)
|
||||||
|
helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC)
|
||||||
|
when (val response = suspendCancellableCoroutine<Any?> { continuation ->
|
||||||
|
helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object :
|
||||||
|
CurrentWeatherCallback {
|
||||||
|
override fun onSuccess(currentWeather: CurrentWeather?) {
|
||||||
|
continuation.resume(currentWeather)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(throwable: Throwable?) {
|
||||||
|
continuation.resume(throwable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
is CurrentWeather -> {
|
||||||
|
Preferences.weatherTemp = response.main.temp.toFloat()
|
||||||
|
Preferences.weatherIcon = response.weather[0].icon
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
is Throwable -> {
|
||||||
|
if (response.javaClass == Throwable::class.java) {
|
||||||
|
// server error, see [OpenWeatherMapHelper.handleCurrentWeatherResponse]
|
||||||
|
if (response.message?.startsWith("UnAuthorized") == true) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
} else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun useWeatherGov(context: Context) {
|
||||||
|
val repository = WeatherGovRepository()
|
||||||
|
val pointsResponse = executeWithRetry(times = 5) {
|
||||||
|
repository.getGridPoints(
|
||||||
|
Preferences.customLocationLat,
|
||||||
|
Preferences.customLocationLon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (pointsResponse) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val pp = pointsResponse.body["properties"] as LinkedTreeMap<*, *>
|
||||||
|
val gridId = pp["gridId"] as String
|
||||||
|
val gridX = pp["gridX"] as Double
|
||||||
|
val gridY = pp["gridY"] as Double
|
||||||
|
|
||||||
|
when (val weatherResponse = repository.getWeather(
|
||||||
|
gridId,
|
||||||
|
gridX,
|
||||||
|
gridY,
|
||||||
|
if (Preferences.weatherTempUnit == "F") "us" else "si"
|
||||||
|
)) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val props =
|
||||||
|
weatherResponse.body["properties"] as LinkedTreeMap<*, *>
|
||||||
|
val periods = props["periods"] as List<*>
|
||||||
|
@android.annotation.SuppressLint("SimpleDateFormat")
|
||||||
|
val format = SimpleDateFormat(
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ssXXX"
|
||||||
|
else
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ssZ"
|
||||||
|
)
|
||||||
|
for (period in periods) {
|
||||||
|
val now = period as LinkedTreeMap<*, *>
|
||||||
|
val endTime = format.parse(now["endTime"] as String)!!
|
||||||
|
if (endTime.time > System.currentTimeMillis()) {
|
||||||
|
val temp = now["temperature"] as Double
|
||||||
|
val fullIcon = now["icon"] as String
|
||||||
|
val isDaytime = now["isDaytime"] as Boolean
|
||||||
|
|
||||||
|
Preferences.weatherTemp = temp.toFloat()
|
||||||
|
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkResponse.ServerError -> {
|
||||||
|
when (pointsResponse.code) {
|
||||||
|
404 -> {
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun useHereProvider(context: Context) {
|
||||||
|
if (Preferences.weatherProviderApiHere != "") {
|
||||||
|
val repository = HereRepository()
|
||||||
|
|
||||||
|
when (val response = repository.getWeather()) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val observations = response.body["observations"] as LinkedTreeMap<*, *>
|
||||||
|
val location = (observations["location"] as List<*>).first() as LinkedTreeMap<*, *>
|
||||||
|
val observation = (location["observation"] as List<*>).first() as LinkedTreeMap<*, *>
|
||||||
|
val iconName = observation["iconName"] as String
|
||||||
|
val daylight = observation["daylight"] as String
|
||||||
|
val temperature = observation["temperature"] as String
|
||||||
|
|
||||||
|
Preferences.weatherTemp = temperature.toFloat()
|
||||||
|
Preferences.weatherIcon = repository.getWeatherIcon(iconName, daylight != "N")
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkResponse.ServerError -> {
|
||||||
|
when (response.code) {
|
||||||
|
401 -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun useWeatherBitProvider(context: Context) {
|
||||||
|
if (Preferences.weatherProviderApiWeatherBit != "") {
|
||||||
|
val repository = WeatherbitRepository()
|
||||||
|
|
||||||
|
when (val response = repository.getWeather()) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val data = response.body["data"] as List<*>?
|
||||||
|
data?.first()?.let { it as LinkedTreeMap<*, *>
|
||||||
|
val temp = it["temp"] as Double
|
||||||
|
val weatherInfo = it["weather"] as LinkedTreeMap<*, *>
|
||||||
|
val iconCode = weatherInfo["icon"] as String
|
||||||
|
|
||||||
|
Preferences.weatherTemp = temp.toFloat()
|
||||||
|
Preferences.weatherIcon = WeatherHelper.getWeatherBitIcon(iconCode)
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkResponse.ServerError -> {
|
||||||
|
when (response.code) {
|
||||||
|
403 -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun useWeatherApiProvider(context: Context) {
|
||||||
|
if (Preferences.weatherProviderApiWeatherApi != "") {
|
||||||
|
val repository = WeatherApiRepository()
|
||||||
|
|
||||||
|
when (val response = repository.getWeather()) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val current = response.body["current"] as LinkedTreeMap<*, *>?
|
||||||
|
current?.let {
|
||||||
|
val tempC = current["temp_c"] as Double
|
||||||
|
val tempF = current["temp_f"] as Double
|
||||||
|
val isDay = current["is_day"] as Double
|
||||||
|
val condition = current["condition"] as LinkedTreeMap<*, *>
|
||||||
|
val iconCode = condition["code"] as Double
|
||||||
|
|
||||||
|
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
|
||||||
|
Preferences.weatherIcon = WeatherHelper.getWeatherApiIcon(iconCode.toInt(), isDay.toInt() == 1)
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkResponse.ServerError -> {
|
||||||
|
when (response.code) {
|
||||||
|
401 -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
403 -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_expired_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun useAccuweatherProvider(context: Context) {
|
||||||
|
if (Preferences.weatherProviderApiAccuweather != "") {
|
||||||
|
val repository = AccuweatherRepository()
|
||||||
|
|
||||||
|
when (val locationResponse = repository.getLocation()) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val key = locationResponse.body["Key"] as String
|
||||||
|
|
||||||
|
when (val weatherResponse = repository.getWeather(key)) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
weatherResponse.body.first().let {
|
||||||
|
val temp = it["Temperature"] as LinkedTreeMap<*, *>
|
||||||
|
val tempC = (temp["Metric"] as LinkedTreeMap<*, *>)["Value"] as Double
|
||||||
|
val tempF = (temp["Imperial"] as LinkedTreeMap<*, *>)["Value"] as Double
|
||||||
|
val isDay = it["IsDayTime"] as Boolean
|
||||||
|
val icon = it["WeatherIcon"] as Double
|
||||||
|
|
||||||
|
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
|
||||||
|
Preferences.weatherIcon = repository.getWeatherIcon(icon.toInt(), isDay)
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkResponse.ServerError -> {
|
||||||
|
when (locationResponse.code) {
|
||||||
|
401 -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
503 -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_expired_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun useYrProvider(context: Context) {
|
||||||
|
val repository = YrRepository()
|
||||||
|
|
||||||
|
when (val response = repository.getWeather()) {
|
||||||
|
is NetworkResponse.Success -> {
|
||||||
|
try {
|
||||||
|
val pp = response.body["properties"] as LinkedTreeMap<*, *>
|
||||||
|
val data = pp["timeseries"] as List<*>?
|
||||||
|
data?.first()?.let { it as LinkedTreeMap<*, *>
|
||||||
|
val dd = it["data"] as LinkedTreeMap<*, *>
|
||||||
|
val instant = dd["instant"] as LinkedTreeMap<*, *>
|
||||||
|
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
|
||||||
|
|
||||||
|
val details = instant["details"] as LinkedTreeMap<*, *>
|
||||||
|
val temp = details["air_temperature"] as Double
|
||||||
|
|
||||||
|
val summary = next["summary"] as LinkedTreeMap<*, *>
|
||||||
|
val iconCode = summary["symbol_code"] as String
|
||||||
|
|
||||||
|
Preferences.weatherTemp = temp.toFloat()
|
||||||
|
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode)
|
||||||
|
Preferences.weatherTempUnit = "C"
|
||||||
|
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
|
||||||
|
Preferences.weatherProviderError = ""
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkResponse.ServerError -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
WeatherHelper.removeWeather(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.api
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponse
|
||||||
|
import retrofit2.http.*
|
||||||
|
|
||||||
|
object ApiServices {
|
||||||
|
interface WeatherGovApiService {
|
||||||
|
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
|
||||||
|
@GET("points/{latitude},{longitude}")
|
||||||
|
suspend fun getGridPoints(
|
||||||
|
@Path("latitude") latitude: String,
|
||||||
|
@Path("longitude") longitude: String
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
|
||||||
|
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
|
||||||
|
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast/hourly")
|
||||||
|
suspend fun getWeather(
|
||||||
|
@Path("gridId") gridId: String,
|
||||||
|
@Path("gridX") gridX: Int,
|
||||||
|
@Path("gridY") gridY: Int,
|
||||||
|
@Query("units") unit: String
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WeatherBitService {
|
||||||
|
@GET("current")
|
||||||
|
suspend fun getWeather(
|
||||||
|
@Query("key") key: String,
|
||||||
|
@Query("lat") lat: String,
|
||||||
|
@Query("lon") lon: String,
|
||||||
|
@Query("units") units: String,
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WeatherApiService {
|
||||||
|
@Headers("Accept: application/json")
|
||||||
|
@GET("current.json")
|
||||||
|
suspend fun getWeather(
|
||||||
|
@Query("key") key: String,
|
||||||
|
@Query("q") location: String,
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HereService {
|
||||||
|
@GET("report.json")
|
||||||
|
suspend fun getWeather(
|
||||||
|
@Query("apiKey") apiKey: String,
|
||||||
|
@Query("latitude") latitude: String,
|
||||||
|
@Query("longitude") longitude: String,
|
||||||
|
@Query("product") product: String,
|
||||||
|
@Query("oneobservation") oneobservation: Boolean,
|
||||||
|
@Query("metric") metric: Boolean,
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccuweatherService {
|
||||||
|
@GET("locations/v1/cities/geoposition/search")
|
||||||
|
suspend fun getLocation(
|
||||||
|
@Query("apikey") apikey: String,
|
||||||
|
@Query("q") location: String
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
|
||||||
|
@GET("currentconditions/v1/{locationKey}")
|
||||||
|
suspend fun getWeather(
|
||||||
|
@Path("locationKey") locationKey: String,
|
||||||
|
@Query("apikey") apikey: String
|
||||||
|
): NetworkResponse<List<HashMap<String, Any>>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface YrService {
|
||||||
|
@Headers("User-Agent: AnotherWidget")
|
||||||
|
@GET("compact.json")
|
||||||
|
suspend fun getWeather(
|
||||||
|
@Query("lat") lat: String,
|
||||||
|
@Query("lon") lon: String,
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeZonesService {
|
||||||
|
@GET("timezoneJSON")
|
||||||
|
suspend fun getTimeZone(
|
||||||
|
@Query("lat") lat: String,
|
||||||
|
@Query("lng") lon: String,
|
||||||
|
@Query("username") username: String = "tommaso.berlose",
|
||||||
|
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class AccuweatherRepository {
|
||||||
|
|
||||||
|
/* ACCUWEATHER */
|
||||||
|
private val apiServiceAccu: ApiServices.AccuweatherService = getRetrofit().create(ApiServices.AccuweatherService::class.java)
|
||||||
|
suspend fun getLocation() = apiServiceAccu.getLocation(Preferences.weatherProviderApiAccuweather, "${Preferences.customLocationLat},${Preferences.customLocationLon}")
|
||||||
|
suspend fun getWeather(locationKey: String) = apiServiceAccu.getWeather(locationKey, Preferences.weatherProviderApiAccuweather)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_ACCU = "https://dataservice.accuweather.com/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_ACCU)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWeatherIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
|
||||||
|
1, 2, 30, 33, 34 -> "01"
|
||||||
|
3, 4, 35, 36 -> "02"
|
||||||
|
5, 37 -> "50"
|
||||||
|
6, 38 -> "03"
|
||||||
|
7, 8 -> "04"
|
||||||
|
11 -> "82"
|
||||||
|
12, 13, 14, 18, 39, 40 -> "10"
|
||||||
|
15 -> "09"
|
||||||
|
16, 17, 41, 42 -> "11"
|
||||||
|
32 -> "80"
|
||||||
|
19, 20, 21, 22, 23, 24, 31, 43, 44 -> "13"
|
||||||
|
25, 26, 29 -> "81"
|
||||||
|
else -> ""
|
||||||
|
} + if (isDaytime) "d" else "n"
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class HereRepository {
|
||||||
|
|
||||||
|
/* HERE */
|
||||||
|
private val apiServiceHere: ApiServices.HereService = getRetrofit().create(ApiServices.HereService::class.java)
|
||||||
|
suspend fun getWeather() = apiServiceHere.getWeather(Preferences.weatherProviderApiHere, Preferences.customLocationLat, Preferences.customLocationLon, "observation", true, Preferences.weatherTempUnit != "F")
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_HERE = "https://weather.ls.hereapi.com/weather/1.0/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_HERE)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWeatherIcon(iconName: String, isDaytime: Boolean): String = when(iconName.substringAfter("night_")) {
|
||||||
|
"sunny" -> "01"
|
||||||
|
"clear" -> "01"
|
||||||
|
"mostly_sunny" -> "01"
|
||||||
|
"mostly_clear" -> "01"
|
||||||
|
"passing_clounds" -> "02"
|
||||||
|
"more_sun_than_clouds" -> "02"
|
||||||
|
"scattered_clouds" -> "02"
|
||||||
|
"partly_cloudy" -> "02"
|
||||||
|
"a_mixture_of_sun_and_clouds" -> "03"
|
||||||
|
"increasing_cloudiness" -> "03"
|
||||||
|
"breaks_of_sun_late" -> "03"
|
||||||
|
"afternoon_clouds" -> "03"
|
||||||
|
"morning_clouds" -> "03"
|
||||||
|
"partly_sunny" -> "03"
|
||||||
|
"high_level_clouds" -> "03"
|
||||||
|
"decreasing_cloudiness" -> "03"
|
||||||
|
"clearing_skies" -> "01"
|
||||||
|
"high_clouds" -> "03"
|
||||||
|
"rain_early" -> "10"
|
||||||
|
"heavy_rain_early" -> "10"
|
||||||
|
"strong_thunderstorms" -> "09"
|
||||||
|
"severe_thunderstorms" -> "09"
|
||||||
|
"thundershowers" -> "11"
|
||||||
|
"thunderstorms" -> "11"
|
||||||
|
"tstorms_early" -> "11"
|
||||||
|
"isolated_tstorms_late" -> "11"
|
||||||
|
"scattered_tstorms_late" -> "11"
|
||||||
|
"tstorms_late" -> "11"
|
||||||
|
"tstorms" -> "11"
|
||||||
|
"ice_fog" -> "82"
|
||||||
|
"more_clouds_than_sun" -> "03"
|
||||||
|
"broken_clouds" -> "03"
|
||||||
|
"scattered_showers" -> "10"
|
||||||
|
"a_few_showers" -> "10"
|
||||||
|
"light_showers" -> "10"
|
||||||
|
"passing_showers" -> "10"
|
||||||
|
"rain_showers" -> "10"
|
||||||
|
"showers" -> "10"
|
||||||
|
"widely_scattered_tstorms" -> "11"
|
||||||
|
"isolated_tstorms" -> "11"
|
||||||
|
"a_few_tstorms" -> "11"
|
||||||
|
"scattered_tstorms" -> "11"
|
||||||
|
"hazy_sunshine" -> "50"
|
||||||
|
"haze" -> "50"
|
||||||
|
"smoke" -> "50"
|
||||||
|
"low_level_haze" -> "50"
|
||||||
|
"early_fog_followed_by_sunny_skies" -> "50"
|
||||||
|
"early_fog" -> "82"
|
||||||
|
"light_fog" -> "82"
|
||||||
|
"fog" -> "82"
|
||||||
|
"dense_fog" -> "82"
|
||||||
|
//"night_haze"
|
||||||
|
//"night_smoke"
|
||||||
|
//"night_low_level_haze"
|
||||||
|
//"night_widely_scattered_tstorms"
|
||||||
|
//"night_isolated_tstorms"
|
||||||
|
//"night_a_few_tstorms"
|
||||||
|
//"night_scattered_tstorms"
|
||||||
|
//"night_tstorms"
|
||||||
|
//"night_clear"
|
||||||
|
"mostly_cloudy" -> "03"
|
||||||
|
"cloudy" -> "04"
|
||||||
|
"overcast" -> "04"
|
||||||
|
"low_clouds" -> "03"
|
||||||
|
"hail" -> "10"
|
||||||
|
"sleet" -> "81"
|
||||||
|
"light_mixture_of_precip" -> "81"
|
||||||
|
"icy_mix" -> "81"
|
||||||
|
"mixture_of_precip" -> "81"
|
||||||
|
"heavy_mixture_of_precip" -> "81"
|
||||||
|
"snow_changing_to_rain" -> "81"
|
||||||
|
"snow_changing_to_an_icy_mix" -> "81"
|
||||||
|
"an_icy_mix_changing_to_snow" -> "81"
|
||||||
|
"an_icy_mix_changing_to_rain" -> "81"
|
||||||
|
"rain_changing_to_snow" -> "81"
|
||||||
|
"rain_changing_to_an_icy_mix" -> "81"
|
||||||
|
"light_icy_mix_early" -> "81"
|
||||||
|
"icy_mix_early" -> "81"
|
||||||
|
"light_icy_mix_late" -> "81"
|
||||||
|
"icy_mix_late" -> "81"
|
||||||
|
"snow_rain_mix" -> "81"
|
||||||
|
"scattered_flurries" -> "13"
|
||||||
|
"snow_flurries" -> "13"
|
||||||
|
"light_snow_showers" -> "13"
|
||||||
|
"snow_showers" -> "13"
|
||||||
|
"light_snow" -> "13"
|
||||||
|
"flurries_early" -> "13"
|
||||||
|
"snow_showers_early" -> "13"
|
||||||
|
"light_snow_early" -> "13"
|
||||||
|
"flurries_late" -> "13"
|
||||||
|
"snow_showers_late" -> "13"
|
||||||
|
"light_snow_late" -> "13"
|
||||||
|
//"night_decreasing_cloudiness"
|
||||||
|
//"night_clearing_skies"
|
||||||
|
//"night_high_level_clouds"
|
||||||
|
//"night_high_clouds"
|
||||||
|
//"night_scattered_showers"
|
||||||
|
//"night_a_few_showers"
|
||||||
|
//"night_light_showers"
|
||||||
|
//"night_passing_showers"
|
||||||
|
//"night_rain_showers"
|
||||||
|
//"night_sprinkles"
|
||||||
|
//"night_showers"
|
||||||
|
//"night_mostly_clear"
|
||||||
|
//"night_passing_clouds"
|
||||||
|
//"night_scattered_clouds"
|
||||||
|
//"night_partly_cloudy"
|
||||||
|
//"increasing_cloudiness"
|
||||||
|
//"night_afternoon_clouds"
|
||||||
|
//"night_morning_clouds"
|
||||||
|
//"night_broken_clouds"
|
||||||
|
//"night_mostly_cloudy"
|
||||||
|
"light_freezing_rain" -> "81"
|
||||||
|
"freezing_rain" -> "81"
|
||||||
|
"heavy_rain" -> "10"
|
||||||
|
"lots_of_rain" -> "10"
|
||||||
|
"tons_of_rain" -> "10"
|
||||||
|
//"heavy_rain_early" -> "10"
|
||||||
|
"heavy_rain_late" -> "10"
|
||||||
|
"flash_floods" -> "10"
|
||||||
|
"flood" -> "10"
|
||||||
|
"drizzle" -> "10"
|
||||||
|
"sprinkles" -> "10"
|
||||||
|
"light_rain" -> "10"
|
||||||
|
"sprinkles_early" -> "10"
|
||||||
|
"light_rain_early" -> "10"
|
||||||
|
"sprinkles_late" -> "10"
|
||||||
|
"light_rain_late" -> "10"
|
||||||
|
"rain" -> "10"
|
||||||
|
"numerous_showers" -> "10"
|
||||||
|
"showery" -> "10"
|
||||||
|
"showers_early" -> "10"
|
||||||
|
//"rain_early" -> "10"
|
||||||
|
"showers_late" -> "10"
|
||||||
|
"rain_late" -> "10"
|
||||||
|
"snow" -> "13"
|
||||||
|
"moderate_snow" -> "13"
|
||||||
|
"snow_early" -> "13"
|
||||||
|
"snow_late" -> "13"
|
||||||
|
"heavy_snow" -> "13"
|
||||||
|
"heavy_snow_early" -> "13"
|
||||||
|
"heavy_snow_late" -> "13"
|
||||||
|
"tornado" -> "80"
|
||||||
|
"tropical_storm" -> "09"
|
||||||
|
"hurricane" -> "80"
|
||||||
|
"sandstorm" -> "50"
|
||||||
|
"duststorm" -> "50"
|
||||||
|
"snowstorm" -> "13"
|
||||||
|
"blizzard" -> "13"
|
||||||
|
else -> ""
|
||||||
|
} + if (isDaytime) "d" else "n"
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class TimeZonesRepository {
|
||||||
|
|
||||||
|
/* YR */
|
||||||
|
private val apiService: ApiServices.TimeZonesService = getRetrofit().create(ApiServices.TimeZonesService::class.java)
|
||||||
|
suspend fun getTimeZone(lat: String, long: String) = apiService.getTimeZone(lat, long)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_YR = "http://api.geonames.org/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_YR)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class WeatherApiRepository {
|
||||||
|
|
||||||
|
/* WEATHER API*/
|
||||||
|
private val apiServiceApi: ApiServices.WeatherApiService = getRetrofit().create(ApiServices.WeatherApiService::class.java)
|
||||||
|
suspend fun getWeather() = apiServiceApi.getWeather(Preferences.weatherProviderApiWeatherApi, "${Preferences.customLocationLat},${Preferences.customLocationLon}")
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_API = "http://api.weatherapi.com/v1/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_API)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class WeatherGovRepository {
|
||||||
|
|
||||||
|
/* WEATHER GOV*/
|
||||||
|
private val apiServiceGov: ApiServices.WeatherGovApiService = getRetrofit().create(ApiServices.WeatherGovApiService::class.java)
|
||||||
|
suspend fun getGridPoints(latitude: String, longitude: String) = apiServiceGov.getGridPoints(latitude, longitude)
|
||||||
|
suspend fun getWeather(gridId: String, gridX: Double, gridY: Double, unit: String) = apiServiceGov.getWeather(gridId, gridX.toInt(), gridY.toInt(), unit)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_GOV = "https://api.weather.gov/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_GOV)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class WeatherbitRepository {
|
||||||
|
|
||||||
|
/* BIT */
|
||||||
|
private val apiServiceBit: ApiServices.WeatherBitService = getRetrofit().create(ApiServices.WeatherBitService::class.java)
|
||||||
|
suspend fun getWeather() = apiServiceBit.getWeather(Preferences.weatherProviderApiWeatherBit, Preferences.customLocationLat, Preferences.customLocationLon, if (Preferences.weatherTempUnit == "F") "I" else "M")
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_BIT = "https://api.weatherbit.io/v2.0/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_BIT)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.network.repository
|
||||||
|
|
||||||
|
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
|
class YrRepository {
|
||||||
|
|
||||||
|
/* YR */
|
||||||
|
private val apiServiceYr: ApiServices.YrService = getRetrofit().create(ApiServices.YrService::class.java)
|
||||||
|
suspend fun getWeather() = apiServiceYr.getWeather(Preferences.customLocationLat, Preferences.customLocationLon)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL_YR = "https://api.met.no/weatherapi/locationforecast/2.0/"
|
||||||
|
|
||||||
|
private fun getRetrofit(): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL_YR)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.receivers
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.BatteryManager
|
||||||
|
import android.util.Log
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
|
||||||
|
class BatteryLevelReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
when(intent.action) {
|
||||||
|
Intent.ACTION_BATTERY_LOW -> Preferences.isBatteryLevelLow = true
|
||||||
|
Intent.ACTION_BATTERY_OKAY -> Preferences.isBatteryLevelLow = false
|
||||||
|
Intent.ACTION_POWER_CONNECTED -> Preferences.isCharging = true
|
||||||
|
Intent.ACTION_POWER_DISCONNECTED -> Preferences.isCharging = false
|
||||||
|
}
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,10 +12,8 @@ class NewCalendarEventReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
val eventRepository = EventRepository(context)
|
val eventRepository = EventRepository(context)
|
||||||
Log.d("ciao", "nuovo evento")
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Intent.ACTION_PROVIDER_CHANGED,
|
Intent.ACTION_PROVIDER_CHANGED -> {
|
||||||
Intent.ACTION_TIME_CHANGED -> {
|
|
||||||
CalendarHelper.updateEventList(context)
|
CalendarHelper.updateEventList(context)
|
||||||
}
|
}
|
||||||
Actions.ACTION_GO_TO_NEXT_EVENT -> {
|
Actions.ACTION_GO_TO_NEXT_EVENT -> {
|
||||||
@ -25,5 +23,6 @@ class NewCalendarEventReceiver : BroadcastReceiver() {
|
|||||||
eventRepository.goToPreviousEvent()
|
eventRepository.goToPreviousEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
eventRepository.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.receivers
|
||||||
|
|
||||||
|
import android.app.*
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.session.MediaSession
|
||||||
|
import android.os.Build
|
||||||
|
import android.service.notification.NotificationListenerService
|
||||||
|
import android.service.notification.StatusBarNotification
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationListener : NotificationListenerService() {
|
||||||
|
override fun onListenerConnected() {
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||||
|
ActiveNotificationsHelper.clearLastNotification(this)
|
||||||
|
super.onListenerConnected()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onListenerDisconnected() {
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||||
|
ActiveNotificationsHelper.clearLastNotification(this)
|
||||||
|
super.onListenerDisconnected()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNotificationPosted(sbn: StatusBarNotification?) {
|
||||||
|
sbn?.notification?.extras?.let { bundle ->
|
||||||
|
bundle.getParcelable<MediaSession.Token>(Notification.EXTRA_MEDIA_SESSION)?.let {
|
||||||
|
if (Preferences.showMusic)
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||||
|
} ?: run {
|
||||||
|
val isGroupHeader = sbn.notification.flags and Notification.FLAG_GROUP_SUMMARY != 0
|
||||||
|
val isOngoing = sbn.notification.flags and Notification.FLAG_ONGOING_EVENT != 0
|
||||||
|
|
||||||
|
if (Preferences.showNotifications && bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing && ActiveNotificationsHelper.isAppAccepted(sbn.packageName) && !sbn.packageName.contains("com.android.systemui")) {
|
||||||
|
Preferences.lastNotificationId = sbn.id
|
||||||
|
Preferences.lastNotificationTitle = bundle.getString(Notification.EXTRA_TITLE) ?: ""
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
Preferences.lastNotificationIcon = sbn.notification.smallIcon.resId
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
Preferences.lastNotificationIcon = sbn.notification.icon
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Preferences.lastNotificationIcon = 0
|
||||||
|
}
|
||||||
|
Preferences.lastNotificationPackage = sbn.packageName
|
||||||
|
MainWidget.updateWidget(this)
|
||||||
|
setTimeout(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onNotificationPosted(sbn)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
|
||||||
|
if (Preferences.showMusic)
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||||
|
sbn?.let {
|
||||||
|
if (Preferences.showNotifications && sbn.id == Preferences.lastNotificationId && sbn.packageName == Preferences.lastNotificationPackage) {
|
||||||
|
ActiveNotificationsHelper.clearLastNotification(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onNotificationRemoved(sbn)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTimeout(context: Context) {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_CLEAR_NOTIFICATION
|
||||||
|
}
|
||||||
|
val timeoutPref = Constants.GlanceNotificationTimer.fromInt(Preferences.hideNotificationAfter)
|
||||||
|
if (timeoutPref != Constants.GlanceNotificationTimer.WHEN_DISMISSED) {
|
||||||
|
setExactIfCanSchedule(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
Calendar.getInstance().timeInMillis + when (timeoutPref) {
|
||||||
|
Constants.GlanceNotificationTimer.HALF_MINUTE -> 30 * 1000
|
||||||
|
Constants.GlanceNotificationTimer.ONE_MINUTE -> 60 * 1000
|
||||||
|
Constants.GlanceNotificationTimer.FIVE_MINUTES -> 5 * 60 * 1000
|
||||||
|
Constants.GlanceNotificationTimer.TEN_MINUTES -> 10 * 60 * 1000
|
||||||
|
Constants.GlanceNotificationTimer.FIFTEEN_MINUTES -> 15 * 60 * 1000
|
||||||
|
else -> 60 * 1000
|
||||||
|
},
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
5,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun clearTimeout(context: Context) {
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_CLEAR_NOTIFICATION
|
||||||
|
}
|
||||||
|
cancel(PendingIntent.getBroadcast(context, 5, intent, PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,20 @@
|
|||||||
package com.tommasoberlose.anotherwidget.receivers
|
package com.tommasoberlose.anotherwidget.receivers
|
||||||
|
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.app.AlarmManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.util.Log
|
|
||||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||||
import com.tommasoberlose.anotherwidget.global.Actions
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.*
|
||||||
|
import com.tommasoberlose.anotherwidget.models.Event
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
import org.joda.time.Period
|
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import org.joda.time.Period
|
||||||
|
|
||||||
|
|
||||||
class UpdatesReceiver : BroadcastReceiver() {
|
class UpdatesReceiver : BroadcastReceiver() {
|
||||||
@ -21,45 +23,175 @@ class UpdatesReceiver : BroadcastReceiver() {
|
|||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Intent.ACTION_BOOT_COMPLETED,
|
Intent.ACTION_BOOT_COMPLETED,
|
||||||
Intent.ACTION_MY_PACKAGE_REPLACED,
|
Intent.ACTION_MY_PACKAGE_REPLACED,
|
||||||
Actions.ACTION_CALENDAR_UPDATE -> CalendarHelper.updateEventList(context)
|
Intent.ACTION_TIME_CHANGED,
|
||||||
|
Intent.ACTION_TIMEZONE_CHANGED,
|
||||||
|
Intent.ACTION_LOCALE_CHANGED -> {
|
||||||
|
CalendarHelper.updateEventList(context)
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||||
|
ActiveNotificationsHelper.clearLastNotification(context)
|
||||||
|
GreetingsHelper.toggleGreetings(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent.ACTION_DATE_CHANGED,
|
||||||
|
Actions.ACTION_CALENDAR_UPDATE -> {
|
||||||
|
CalendarHelper.updateEventList(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
Actions.ACTION_TIME_UPDATE -> {
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
if (intent.hasExtra(EVENT_ID)) {
|
||||||
|
setUpdates(context, intent.getLongExtra(EVENT_ID, -1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"com.sec.android.widgetapp.APPWIDGET_RESIZE",
|
"com.sec.android.widgetapp.APPWIDGET_RESIZE",
|
||||||
Intent.ACTION_DATE_CHANGED,
|
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED,
|
||||||
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED -> MainWidget.updateWidget(context)
|
Actions.ACTION_ALARM_UPDATE,
|
||||||
|
Actions.ACTION_UPDATE_GREETINGS -> {
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
Actions.ACTION_CLEAR_NOTIFICATION -> {
|
||||||
|
ActiveNotificationsHelper.clearLastNotification(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
Actions.ACTION_REFRESH -> {
|
||||||
|
CalendarHelper.updateEventList(context)
|
||||||
|
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||||
|
WeatherHelper.updateWeather(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun setUpdates(context: Context) {
|
const val EVENT_ID = "EVENT_ID"
|
||||||
removeUpdates(context)
|
|
||||||
|
|
||||||
|
fun setUpdates(context: Context, eventId: Long? = null) {
|
||||||
|
if (!Preferences.showEvents)
|
||||||
|
return
|
||||||
val eventRepository = EventRepository(context)
|
val eventRepository = EventRepository(context)
|
||||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
if (eventId == null) {
|
||||||
eventRepository.getEvents().forEach { event ->
|
// schedule ACTION_CALENDAR_UPDATE at midnight (ACTION_DATE_CHANGED no longer works)
|
||||||
val hoursDiff = Period(Calendar.getInstance().timeInMillis, event.startDate).hours
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
setExactIfCanSchedule(
|
||||||
// Update the widget every hour till the event
|
AlarmManager.RTC,
|
||||||
(0 .. hoursDiff).forEach {
|
Calendar.getInstance().apply {
|
||||||
setExact(
|
set(Calendar.MILLISECOND, 0)
|
||||||
AlarmManager.RTC_WAKEUP,
|
set(Calendar.SECOND, 0)
|
||||||
(event.startDate + 1000) - it * 1000 * 60* 60,
|
set(Calendar.MINUTE, 0)
|
||||||
PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java).apply { action = Actions.ACTION_TIME_UPDATE }, 0)
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
add(Calendar.DATE, 1)
|
||||||
|
}.timeInMillis,
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_CALENDAR_UPDATE
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// Update the widget one second after the event is finished
|
|
||||||
setExact(
|
|
||||||
AlarmManager.RTC_WAKEUP,
|
|
||||||
event.endDate + 1000,
|
|
||||||
PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java).apply { action = Actions.ACTION_TIME_UPDATE }, 0)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eventRepository.getFutureEvents().forEach { event ->
|
||||||
|
setEventUpdate(context, event)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val event = eventRepository.getEventById(eventId)
|
||||||
|
if (event != null) {
|
||||||
|
setEventUpdate(context, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventRepository.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setEventUpdate(context: Context, event: Event) {
|
||||||
|
val now = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}
|
||||||
|
val diff = Period(now.timeInMillis, event.startDate, org.joda.time.PeriodType.time())
|
||||||
|
val limit = when (Preferences.showUntil) {
|
||||||
|
0 -> 1000 * 60 * 60 * 3
|
||||||
|
1 -> 1000 * 60 * 60 * 6
|
||||||
|
2 -> 1000 * 60 * 60 * 12
|
||||||
|
3 -> 1000 * 60 * 60 * 24
|
||||||
|
4 -> 1000 * 60 * 60 * 24 * 3
|
||||||
|
5 -> 1000 * 60 * 60 * 24 * 7
|
||||||
|
6 -> 1000 * 60 * 30
|
||||||
|
7 -> 1000 * 60 * 60
|
||||||
|
else -> 1000 * 60 * 60 * 6
|
||||||
|
}
|
||||||
|
val fireTime = when {
|
||||||
|
event.startDate <= now.timeInMillis
|
||||||
|
-> event.endDate
|
||||||
|
event.startDate > now.timeInMillis + limit
|
||||||
|
-> event.startDate - limit
|
||||||
|
!Preferences.showDiffTime
|
||||||
|
-> return
|
||||||
|
event.allDay
|
||||||
|
-> event.startDate
|
||||||
|
diff.hours > 12
|
||||||
|
-> event.startDate - 12 * 1000 * 60 * 60 + 1000 * 60
|
||||||
|
diff.hours > 0
|
||||||
|
-> event.startDate - diff.hours * 1000 * 60 * 60 + 1000 * 60
|
||||||
|
else
|
||||||
|
-> event.startDate - 1000 * 60 * when (Preferences.widgetUpdateFrequency) {
|
||||||
|
Constants.WidgetUpdateFrequency.DEFAULT.rawValue -> {
|
||||||
|
when {
|
||||||
|
diff.minutes >= 45 -> 44
|
||||||
|
diff.minutes >= 30 -> 29
|
||||||
|
diff.minutes >= 15 -> 14
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Constants.WidgetUpdateFrequency.HIGH.rawValue -> {
|
||||||
|
when {
|
||||||
|
diff.minutes >= 5 -> diff.minutes - diff.minutes % 5 - 1
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no need to schedule updates after the next ACTION_CALENDAR_UPDATE
|
||||||
|
if (Calendar.getInstance().apply {
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
add(Calendar.DATE, 1)
|
||||||
|
}.timeInMillis <= fireTime) return
|
||||||
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
|
setExactIfCanSchedule(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
fireTime.coerceAtLeast(now.timeInMillis + 1000 * 60),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
event.id.toInt(),
|
||||||
|
Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_TIME_UPDATE
|
||||||
|
if (event.startDate > now.timeInMillis)
|
||||||
|
putExtra(EVENT_ID, event.id)
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeUpdates(context: Context) {
|
fun removeUpdates(context: Context) {
|
||||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||||
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java), 0))
|
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_CALENDAR_UPDATE
|
||||||
|
}, PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
val eventRepository = EventRepository(context)
|
||||||
|
eventRepository.getFutureEvents().forEach {
|
||||||
|
cancel(PendingIntent.getBroadcast(context, it.id.toInt(), Intent(context, UpdatesReceiver::class.java).apply {
|
||||||
|
action = Actions.ACTION_TIME_UPDATE
|
||||||
|
}, PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
}
|
||||||
|
eventRepository.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package com.tommasoberlose.anotherwidget.receivers
|
package com.tommasoberlose.anotherwidget.receivers
|
||||||
|
|
||||||
import android.app.AlarmManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.tommasoberlose.anotherwidget.global.Actions
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||||
import java.util.*
|
import com.tommasoberlose.anotherwidget.services.WeatherWorker
|
||||||
|
|
||||||
|
|
||||||
class WeatherReceiver : BroadcastReceiver() {
|
class WeatherReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
@ -17,68 +14,24 @@ class WeatherReceiver : BroadcastReceiver() {
|
|||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Intent.ACTION_BOOT_COMPLETED,
|
Intent.ACTION_BOOT_COMPLETED,
|
||||||
Intent.ACTION_MY_PACKAGE_REPLACED,
|
Intent.ACTION_MY_PACKAGE_REPLACED,
|
||||||
Intent.ACTION_TIME_CHANGED -> setUpdates(context)
|
Intent.ACTION_TIMEZONE_CHANGED,
|
||||||
|
Intent.ACTION_LOCALE_CHANGED,
|
||||||
Actions.ACTION_WEATHER_UPDATE -> WeatherHelper.updateWeather(context)
|
Intent.ACTION_TIME_CHANGED,
|
||||||
|
Actions.ACTION_WEATHER_UPDATE -> {
|
||||||
|
WeatherHelper.updateWeather(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun setUpdates(context: Context) {
|
fun setUpdates(context: Context) {
|
||||||
removeUpdates(context)
|
if (Preferences.showWeather) {
|
||||||
|
WeatherWorker.enqueueTrigger(context)
|
||||||
if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
|
|
||||||
WeatherHelper.updateWeather(context)
|
|
||||||
|
|
||||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
|
||||||
val pi = PendingIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
1,
|
|
||||||
Intent(context, WeatherReceiver::class.java).apply {
|
|
||||||
action = Actions.ACTION_WEATHER_UPDATE
|
|
||||||
},
|
|
||||||
0
|
|
||||||
)
|
|
||||||
|
|
||||||
val refresh: Long = when (Preferences.weatherRefreshPeriod) {
|
|
||||||
0 -> 30
|
|
||||||
1 -> 60
|
|
||||||
2 -> 60 * 3
|
|
||||||
3 -> 60 * 6
|
|
||||||
4 -> 60 * 12
|
|
||||||
5 -> 60 * 24
|
|
||||||
else -> 60
|
|
||||||
}
|
|
||||||
val now = Calendar.getInstance().apply {
|
|
||||||
set(Calendar.MILLISECOND, 0)
|
|
||||||
set(Calendar.SECOND, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
setRepeating(AlarmManager.RTC_WAKEUP, now.timeInMillis, 1000 * 60 * refresh, pi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setOneTimeUpdate(context: Context) {
|
|
||||||
// Update the weather in a few minuter when the api key has been changed
|
|
||||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
|
||||||
val pi = PendingIntent.getBroadcast(context, 1, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0)
|
|
||||||
val now = Calendar.getInstance().apply {
|
|
||||||
set(Calendar.MILLISECOND, 0)
|
|
||||||
set(Calendar.SECOND, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
listOf(10, 15, 20).forEach {
|
|
||||||
setExact(AlarmManager.RTC_WAKEUP, now.timeInMillis + 1000 * 60 * it, pi)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeUpdates(context: Context) {
|
fun removeUpdates(context: Context) {
|
||||||
val intent = Intent(context, WeatherReceiver::class.java)
|
WeatherWorker.cancelTrigger(context)
|
||||||
val sender = PendingIntent.getBroadcast(context, 1, intent, 0)
|
|
||||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
||||||
alarmManager.cancel(sender)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import android.content.Intent
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
import com.tommasoberlose.anotherwidget.global.Actions
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||||
import com.tommasoberlose.anotherwidget.utils.toast
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
|
||||||
@ -15,19 +16,25 @@ class WidgetClickListenerReceiver : BroadcastReceiver() {
|
|||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (intent.action == Actions.ACTION_OPEN_WEATHER_INTENT) {
|
if (intent.action == Actions.ACTION_OPEN_WEATHER_INTENT) {
|
||||||
try {
|
try {
|
||||||
context.startActivity(IntentHelper.getWeatherIntent(context))
|
IntentHelper.getWeatherIntent(context).run {
|
||||||
|
if (flags and Intent.FLAG_ACTIVITY_NEW_TASK == Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
context.startActivity(this)
|
||||||
|
else
|
||||||
|
context.sendBroadcast(this)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
val uri = Uri.parse("https://yandex.ru/pogoda")
|
||||||
|
.buildUpon()
|
||||||
|
.appendQueryParameter("lat", Preferences.customLocationLat)
|
||||||
|
.appendQueryParameter("lon", Preferences.customLocationLon)
|
||||||
|
.build()
|
||||||
|
val i = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
try {
|
try {
|
||||||
context.applicationContext.startActivity(IntentHelper.getWeatherIntent(context.applicationContext))
|
context.startActivity(i)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
val uri = Uri.parse("http://www.google.com/#q=weather")
|
context.toast(context.getString(R.string.error_opening_app))
|
||||||
val i = Intent(Intent.ACTION_VIEW, uri)
|
|
||||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
try {
|
|
||||||
context.startActivity(i)
|
|
||||||
} catch (ignored: Exception) {
|
|
||||||
context.toast(context.getString(R.string.error_opening_app))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.services
|
|
||||||
|
|
||||||
import android.app.job.JobInfo
|
|
||||||
import android.app.job.JobInfo.TriggerContentUri
|
|
||||||
import android.app.job.JobParameters
|
|
||||||
import android.app.job.JobScheduler
|
|
||||||
import android.app.job.JobService
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.provider.CalendarContract
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
|
||||||
|
|
||||||
|
|
||||||
class EventListenerJob : JobService() {
|
|
||||||
override fun onStartJob(params: JobParameters): Boolean {
|
|
||||||
CalendarHelper.updateEventList(this)
|
|
||||||
schedule(
|
|
||||||
this
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun onStopJob(params: JobParameters): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val jobId = 1005
|
|
||||||
fun schedule(context: Context) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
val componentName = ComponentName(
|
|
||||||
context,
|
|
||||||
EventListenerJob::class.java
|
|
||||||
)
|
|
||||||
with(context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler) {
|
|
||||||
schedule(
|
|
||||||
JobInfo.Builder(jobId, componentName)
|
|
||||||
.addTriggerContentUri(TriggerContentUri(
|
|
||||||
CalendarContract.CONTENT_URI,
|
|
||||||
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(context: Context) {
|
|
||||||
val js = context.getSystemService(JobScheduler::class.java)
|
|
||||||
js?.cancel(jobId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,199 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.services
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.Worker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
|
||||||
|
import com.tommasoberlose.anotherwidget.models.Event
|
||||||
|
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
|
import java.util.*
|
||||||
|
import me.everything.providers.android.calendar.CalendarProvider
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
|
class UpdateCalendarWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
|
||||||
|
override fun doWork(): Result {
|
||||||
|
val context = applicationContext
|
||||||
|
UpdatesReceiver.removeUpdates(context)
|
||||||
|
val eventRepository = EventRepository(context)
|
||||||
|
|
||||||
|
if (Preferences.showEvents) {
|
||||||
|
if (!context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
|
||||||
|
eventRepository.resetNextEventData()
|
||||||
|
eventRepository.clearEvents()
|
||||||
|
} else {
|
||||||
|
// fetch all events from now to next ACTION_CALENDAR_UPDATE + limit
|
||||||
|
val now = Calendar.getInstance()
|
||||||
|
val limit = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
add(Calendar.DATE, 1)
|
||||||
|
when (Preferences.showUntil) {
|
||||||
|
0 -> add(Calendar.HOUR, 3)
|
||||||
|
1 -> add(Calendar.HOUR, 6)
|
||||||
|
2 -> add(Calendar.HOUR, 12)
|
||||||
|
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||||
|
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||||
|
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||||
|
6 -> add(Calendar.MINUTE, 30)
|
||||||
|
7 -> add(Calendar.HOUR, 1)
|
||||||
|
else -> add(Calendar.HOUR, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val eventList = ArrayList<Event>()
|
||||||
|
val provider = CalendarProvider(context)
|
||||||
|
// apply time zone offset to correctly fetch all-day events
|
||||||
|
val data = provider.getInstances(
|
||||||
|
now.timeInMillis + now.timeZone.getOffset(now.timeInMillis).coerceAtMost(0),
|
||||||
|
limit.timeInMillis + limit.timeZone.getOffset(limit.timeInMillis).coerceAtLeast(0)
|
||||||
|
)
|
||||||
|
if (data != null) {
|
||||||
|
val filteredCalendarIdList = CalendarHelper.getFilteredCalendarIdList()
|
||||||
|
for (instance in data.list) {
|
||||||
|
try {
|
||||||
|
val e = provider.getEvent(instance.eventId)
|
||||||
|
if (e == null || e.deleted || filteredCalendarIdList.contains(e.calendarId))
|
||||||
|
continue
|
||||||
|
if (e.allDay) {
|
||||||
|
val start = Calendar.getInstance()
|
||||||
|
start.timeInMillis = instance.begin
|
||||||
|
val end = Calendar.getInstance()
|
||||||
|
end.timeInMillis = instance.end
|
||||||
|
instance.begin =
|
||||||
|
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
|
||||||
|
instance.end =
|
||||||
|
end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
|
||||||
|
}
|
||||||
|
if (instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end) {
|
||||||
|
/* Following check may result in "fake" all-day events with
|
||||||
|
* non-UTC start/end time, and therefore cannot be found by
|
||||||
|
* Calendar when tapped to open details.
|
||||||
|
// Check all day events
|
||||||
|
val startDate = Calendar.getInstance()
|
||||||
|
startDate.timeInMillis = instance.begin
|
||||||
|
val endDate = Calendar.getInstance()
|
||||||
|
endDate.timeInMillis = instance.end
|
||||||
|
|
||||||
|
val isAllDay = e.allDay || (
|
||||||
|
startDate.get(Calendar.MILLISECOND) == 0
|
||||||
|
&& startDate.get(Calendar.SECOND) == 0
|
||||||
|
&& startDate.get(Calendar.MINUTE) == 0
|
||||||
|
&& startDate.get(Calendar.HOUR_OF_DAY) == 0
|
||||||
|
&& endDate.get(Calendar.MILLISECOND) == 0
|
||||||
|
&& endDate.get(Calendar.SECOND) == 0
|
||||||
|
&& endDate.get(Calendar.MINUTE) == 0
|
||||||
|
&& endDate.get(Calendar.HOUR_OF_DAY) == 0
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
eventList.add(
|
||||||
|
Event(
|
||||||
|
id = instance.id,
|
||||||
|
eventID = e.id,
|
||||||
|
title = e.title ?: "",
|
||||||
|
startDate = instance.begin,
|
||||||
|
endDate = instance.end,
|
||||||
|
calendarID = e.calendarId,
|
||||||
|
allDay = e.allDay,
|
||||||
|
address = e.eventLocation ?: "",
|
||||||
|
selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
|
||||||
|
availability = e.availability
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortedEvents = eventList.sortEvents()
|
||||||
|
val filteredEventList = sortedEvents.applyFilters()
|
||||||
|
|
||||||
|
if (filteredEventList.isEmpty()) {
|
||||||
|
eventRepository.resetNextEventData()
|
||||||
|
eventRepository.clearEvents()
|
||||||
|
} else {
|
||||||
|
val first = filteredEventList.first()
|
||||||
|
if (Preferences.nextEventId != first.id && (
|
||||||
|
//Preferences.showWeatherAsGlanceProvider || !Preferences.showNextEvent ||
|
||||||
|
eventRepository.getEventById(first.id)?.startDate != first.startDate))
|
||||||
|
eventRepository.saveNextEventData(first)
|
||||||
|
eventRepository.saveEvents(filteredEventList)
|
||||||
|
}
|
||||||
|
} catch (ignored: java.lang.Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eventRepository.resetNextEventData()
|
||||||
|
eventRepository.clearEvents()
|
||||||
|
}
|
||||||
|
eventRepository.close()
|
||||||
|
UpdatesReceiver.setUpdates(context)
|
||||||
|
|
||||||
|
MainWidget.updateWidget(context)
|
||||||
|
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||||
|
|
||||||
|
if (Preferences.showEvents)
|
||||||
|
enqueueTrigger(context)
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun enqueue(context: Context) {
|
||||||
|
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||||
|
"updateEventList",
|
||||||
|
ExistingWorkPolicy.KEEP,
|
||||||
|
OneTimeWorkRequestBuilder<UpdateCalendarWorker>().build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enqueueTrigger(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||||
|
"updateEventListTrigger",
|
||||||
|
ExistingWorkPolicy.KEEP,
|
||||||
|
OneTimeWorkRequestBuilder<Trigger>().setConstraints(
|
||||||
|
Constraints.Builder().addContentUriTrigger(
|
||||||
|
CalendarContract.CONTENT_URI,
|
||||||
|
true
|
||||||
|
).build()
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelTrigger(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
WorkManager.getInstance(context).cancelUniqueWork(
|
||||||
|
"updateEventListTrigger"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Trigger(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
override fun doWork(): Result {
|
||||||
|
if (Preferences.showEvents && !isStopped)
|
||||||
|
enqueue(applicationContext)
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.services
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.location.Location
|
||||||
|
import android.location.LocationManager
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.Worker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class WeatherWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
val context = applicationContext
|
||||||
|
if (Preferences.customLocationAdd == "" &&
|
||||||
|
context.checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||||
|
) {
|
||||||
|
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
var location: Location? = null
|
||||||
|
for (provider in lm.getProviders(true)) {
|
||||||
|
lm.getLastKnownLocation(provider)?.let {
|
||||||
|
if (location == null ||
|
||||||
|
it.time - location!!.time > 2 * 60 * 1000 ||
|
||||||
|
(it.time - location!!.time > -2 * 60 * 1000 && it.accuracy < location!!.accuracy))
|
||||||
|
location = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
location?.let { location ->
|
||||||
|
Preferences.customLocationLat = location.latitude.toString()
|
||||||
|
Preferences.customLocationLon = location.longitude.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
WeatherNetworkApi(context).updateWeather()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Preferences.showWeather)
|
||||||
|
enqueueTrigger(context)
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun enqueue(context: Context, replace: Boolean = false) {
|
||||||
|
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||||
|
"updateWeather",
|
||||||
|
if (replace) ExistingWorkPolicy.REPLACE else ExistingWorkPolicy.KEEP,
|
||||||
|
OneTimeWorkRequestBuilder<WeatherWorker>().build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enqueueTrigger(context: Context) {
|
||||||
|
val interval = when (Preferences.weatherRefreshPeriod) {
|
||||||
|
0 -> 30
|
||||||
|
1 -> 60
|
||||||
|
2 -> 60L * 3
|
||||||
|
3 -> 60L * 6
|
||||||
|
4 -> 60L * 12
|
||||||
|
5 -> 60L * 24
|
||||||
|
else -> 60
|
||||||
|
}
|
||||||
|
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||||
|
"updateWeatherTrigger",
|
||||||
|
ExistingWorkPolicy.REPLACE,
|
||||||
|
OneTimeWorkRequestBuilder<Trigger>().setInitialDelay(
|
||||||
|
interval, TimeUnit.MINUTES
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelTrigger(context: Context) {
|
||||||
|
WorkManager.getInstance(context).cancelUniqueWork(
|
||||||
|
"updateWeatherTrigger"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Trigger(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
override fun doWork(): Result {
|
||||||
|
if (Preferences.showWeather && !isStopped)
|
||||||
|
enqueue(applicationContext)
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,135 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.activities
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.ApplicationInfo
|
|
||||||
import android.content.pm.ResolveInfo
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ImageView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Constants
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel
|
|
||||||
import kotlinx.android.synthetic.main.activity_choose_application.*
|
|
||||||
import kotlinx.android.synthetic.main.activity_choose_application.list_view
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import net.idik.lib.slimadapter.SlimAdapter
|
|
||||||
|
|
||||||
|
|
||||||
class ChooseApplicationActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
private lateinit var adapter: SlimAdapter
|
|
||||||
private lateinit var viewModel: ChooseApplicationViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this).get(ChooseApplicationViewModel::class.java)
|
|
||||||
val binding = DataBindingUtil.setContentView<ActivityChooseApplicationBinding>(this, R.layout.activity_choose_application)
|
|
||||||
|
|
||||||
list_view.setHasFixedSize(true)
|
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
|
||||||
list_view.layoutManager = mLayoutManager
|
|
||||||
|
|
||||||
adapter = SlimAdapter.create()
|
|
||||||
adapter
|
|
||||||
.register<String>(R.layout.application_info_layout) { _, injector ->
|
|
||||||
injector
|
|
||||||
.text(R.id.text, getString(R.string.default_name))
|
|
||||||
.image(R.id.icon, R.drawable.round_add_to_home_screen)
|
|
||||||
.with<ImageView>(R.id.icon) {
|
|
||||||
it.scaleX = 0.8f
|
|
||||||
it.scaleY = 0.8f
|
|
||||||
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
|
|
||||||
}
|
|
||||||
.clicked(R.id.item) {
|
|
||||||
val resultIntent = Intent()
|
|
||||||
resultIntent.putExtra(Constants.RESULT_APP_NAME, "")
|
|
||||||
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, "")
|
|
||||||
setResult(Activity.RESULT_OK, resultIntent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.register<ResolveInfo>(R.layout.application_info_layout) { item, injector ->
|
|
||||||
injector
|
|
||||||
.text(R.id.text, item.loadLabel(viewModel.pm))
|
|
||||||
.with<ImageView>(R.id.icon) {
|
|
||||||
Glide
|
|
||||||
.with(this)
|
|
||||||
.load(item.loadIcon(viewModel.pm))
|
|
||||||
.centerCrop()
|
|
||||||
.into(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
injector.clicked(R.id.item) {
|
|
||||||
saveApp(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.attachTo(list_view)
|
|
||||||
|
|
||||||
setupListener()
|
|
||||||
subscribeUi(binding, viewModel)
|
|
||||||
|
|
||||||
search.requestFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var filterJob: Job? = null
|
|
||||||
|
|
||||||
private fun subscribeUi(binding: ActivityChooseApplicationBinding, viewModel: ChooseApplicationViewModel) {
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
|
|
||||||
viewModel.appList.observe(this, Observer {
|
|
||||||
updateList(list = it)
|
|
||||||
loader.visibility = View.INVISIBLE
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.searchInput.observe(this, Observer { search ->
|
|
||||||
updateList(search = search)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
|
|
||||||
loader.visibility = View.VISIBLE
|
|
||||||
filterJob?.cancel()
|
|
||||||
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
if (list != null && list.isNotEmpty()) {
|
|
||||||
delay(200)
|
|
||||||
val filteredList: List<ResolveInfo> = if (search == null || search == "") {
|
|
||||||
list
|
|
||||||
} else {
|
|
||||||
list.filter {
|
|
||||||
it.loadLabel(viewModel.pm).contains(search, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
adapter.updateData(listOf("Default") + filteredList)
|
|
||||||
loader.visibility = View.INVISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
|
||||||
action_back.setOnClickListener {
|
|
||||||
onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveApp(app: ResolveInfo) {
|
|
||||||
val resultIntent = Intent()
|
|
||||||
resultIntent.putExtra(Constants.RESULT_APP_NAME, app.loadLabel(viewModel.pm))
|
|
||||||
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, app.activityInfo.packageName)
|
|
||||||
setResult(Activity.RESULT_OK, resultIntent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.activities
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.location.Address
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.chibatching.kotpref.bulk
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomDateBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.DateHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomDateViewModel
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
|
||||||
import kotlinx.android.synthetic.main.activity_custom_date.*
|
|
||||||
import kotlinx.android.synthetic.main.activity_custom_location.action_back
|
|
||||||
import kotlinx.android.synthetic.main.activity_custom_location.list_view
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import net.idik.lib.slimadapter.SlimAdapter
|
|
||||||
import java.lang.Exception
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class CustomDateActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
private lateinit var adapter: SlimAdapter
|
|
||||||
private lateinit var viewModel: CustomDateViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this).get(CustomDateViewModel::class.java)
|
|
||||||
val binding = DataBindingUtil.setContentView<ActivityCustomDateBinding>(this, R.layout.activity_custom_date)
|
|
||||||
|
|
||||||
|
|
||||||
list_view.setHasFixedSize(true)
|
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
|
||||||
list_view.layoutManager = mLayoutManager
|
|
||||||
|
|
||||||
adapter = SlimAdapter.create()
|
|
||||||
adapter
|
|
||||||
.register<String>(R.layout.custom_date_example_item) { item, injector ->
|
|
||||||
injector
|
|
||||||
.text(R.id.custom_date_example_format, item)
|
|
||||||
.text(R.id.custom_date_example_value, SimpleDateFormat(item, Locale.getDefault()).format(DATE.time))
|
|
||||||
}
|
|
||||||
.attachTo(list_view)
|
|
||||||
|
|
||||||
adapter.updateData(
|
|
||||||
listOf(
|
|
||||||
"d", "dd", "EE", "EEEE", "MM", "MMM", "MMMM", "yy", "yyyy"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
setupListener()
|
|
||||||
subscribeUi(binding, viewModel)
|
|
||||||
|
|
||||||
date_format.requestFocus()
|
|
||||||
|
|
||||||
}
|
|
||||||
private var formatJob: Job? = null
|
|
||||||
|
|
||||||
private fun subscribeUi(binding: ActivityCustomDateBinding, viewModel: CustomDateViewModel) {
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
|
|
||||||
viewModel.dateInput.observe(this, Observer { dateFormat ->
|
|
||||||
formatJob?.cancel()
|
|
||||||
formatJob = lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
loader.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(200)
|
|
||||||
val text = if (dateFormat != "") {
|
|
||||||
try {
|
|
||||||
SimpleDateFormat(dateFormat, Locale.getDefault()).format(DATE.time)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
ERROR_STRING
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"__"
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
action_save.isVisible = text != ERROR_STRING
|
|
||||||
loader.visibility = View.INVISIBLE
|
|
||||||
date_format_value.text = text
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
|
||||||
action_back.setOnClickListener {
|
|
||||||
onBackPressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_save.setOnClickListener {
|
|
||||||
Preferences.dateFormat = viewModel.dateInput.value ?: ""
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_date_format_info.setOnClickListener {
|
|
||||||
openURI("https://developer.android.com/reference/java/text/SimpleDateFormat")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ERROR_STRING = "--"
|
|
||||||
val DATE: Calendar = Calendar.getInstance().apply {
|
|
||||||
set(Calendar.MONTH, 10)
|
|
||||||
set(Calendar.DAY_OF_MONTH, 1)
|
|
||||||
set(Calendar.YEAR, 1993)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,190 +1,78 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.activities
|
package com.tommasoberlose.anotherwidget.ui.activities
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.animation.addListener
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.navigation.NavController
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import androidx.navigation.Navigation
|
||||||
|
import com.karumi.dexter.Dexter
|
||||||
|
import com.karumi.dexter.MultiplePermissionsReport
|
||||||
|
import com.karumi.dexter.PermissionToken
|
||||||
|
import com.karumi.dexter.listener.PermissionRequest
|
||||||
|
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityMainBinding
|
||||||
import com.tommasoberlose.anotherwidget.global.Actions
|
import com.tommasoberlose.anotherwidget.global.Actions
|
||||||
import com.tommasoberlose.anotherwidget.global.Constants
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.global.RequestCode
|
import com.tommasoberlose.anotherwidget.global.RequestCode
|
||||||
import com.tommasoberlose.anotherwidget.helpers.BitmapHelper
|
import com.tommasoberlose.anotherwidget.ui.activities.tabs.WeatherProviderActivity
|
||||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
import com.tommasoberlose.anotherwidget.utils.getCurrentWallpaper
|
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
|
||||||
import kotlinx.android.synthetic.main.the_widget_sans.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private var mAppWidgetId: Int = -1
|
private var mAppWidgetId: Int = -1
|
||||||
private lateinit var viewModel: MainViewModel
|
private lateinit var viewModel: MainViewModel
|
||||||
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
private val mainNavController: NavController? by lazy {
|
||||||
|
Navigation.findNavController(
|
||||||
|
this,
|
||||||
|
R.id.content_fragment
|
||||||
|
)
|
||||||
|
}
|
||||||
|
private val settingsNavController: NavController? by lazy {
|
||||||
|
Navigation.findNavController(
|
||||||
|
this,
|
||||||
|
R.id.settings_fragment
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
overridePendingTransition(R.anim.nav_default_enter_anim, R.anim.nav_default_exit_anim)
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||||
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
controlExtras(intent)
|
controlExtras(intent)
|
||||||
|
if (Preferences.showWallpaper) {
|
||||||
// Viewpager
|
requirePermission()
|
||||||
pager.adapter = ViewPagerAdapter(this)
|
|
||||||
pager.offscreenPageLimit = 4
|
|
||||||
TabLayoutMediator(tabs, pager) { tab, position ->
|
|
||||||
tab.text = when (position) {
|
|
||||||
0 -> getString(R.string.settings_general_title)
|
|
||||||
1 -> getString(R.string.settings_calendar_title)
|
|
||||||
2 -> getString(R.string.settings_weather_title)
|
|
||||||
3 -> getString(R.string.settings_clock_title)
|
|
||||||
4 -> getString(R.string.advanced_settings_title)
|
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
}.attach()
|
|
||||||
|
|
||||||
// Init clock
|
|
||||||
time.setTextColor(ColorHelper.getFontColor())
|
|
||||||
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(this@MainActivity))
|
|
||||||
time.isVisible = Preferences.showClock
|
|
||||||
|
|
||||||
preview.layoutParams = preview.layoutParams.apply {
|
|
||||||
height = 160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(this@MainActivity) else 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
|
setContentView(binding.root)
|
||||||
subscribeUi(viewModel)
|
|
||||||
updateUI()
|
|
||||||
|
|
||||||
CalendarHelper.updateEventList(this)
|
|
||||||
WeatherHelper.updateWeather(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var uiJob: Job? = null
|
|
||||||
|
|
||||||
private fun updateUI() {
|
|
||||||
preview.setCardBackgroundColor(getColor(if (ColorHelper.getFontColor().isColorDark()) android.R.color.white else R.color.colorAccent))
|
|
||||||
val generatedView = MainWidget.generateWidgetView(this@MainActivity)
|
|
||||||
generatedView.measure(0, 0)
|
|
||||||
preview.measure(0, 0)
|
|
||||||
|
|
||||||
uiJob?.cancel()
|
|
||||||
uiJob = lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val bitmap = BitmapHelper.getBitmapFromView(generatedView, if (preview.width > 0) preview.width else generatedView.measuredWidth, generatedView.measuredHeight)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
// Clock
|
|
||||||
time.setTextColor(ColorHelper.getFontColor())
|
|
||||||
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(this@MainActivity))
|
|
||||||
time.format12Hour = "hh:mm"
|
|
||||||
|
|
||||||
// Clock bottom margin
|
|
||||||
clock_bottom_margin_none.isVisible = Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
|
|
||||||
clock_bottom_margin_small.isVisible = Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
|
|
||||||
clock_bottom_margin_medium.isVisible = Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
|
|
||||||
clock_bottom_margin_large.isVisible = Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
|
|
||||||
|
|
||||||
if ((Preferences.showClock && !time.isVisible) || (!Preferences.showClock && time.isVisible)) {
|
|
||||||
if (Preferences.showClock) {
|
|
||||||
time.layoutParams = time.layoutParams.apply {
|
|
||||||
height = RelativeLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
}
|
|
||||||
time.measure(0, 0)
|
|
||||||
}
|
|
||||||
val initialHeight = time.measuredHeight
|
|
||||||
ValueAnimator.ofFloat(
|
|
||||||
if (Preferences.showClock) 0f else 1f,
|
|
||||||
if (Preferences.showClock) 1f else 0f
|
|
||||||
).apply {
|
|
||||||
duration = 500L
|
|
||||||
addUpdateListener {
|
|
||||||
val animatedValue = animatedValue as Float
|
|
||||||
time.layoutParams = time.layoutParams.apply {
|
|
||||||
height = (initialHeight * animatedValue).toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addListener(
|
|
||||||
onStart = {
|
|
||||||
if (Preferences.showClock) {
|
|
||||||
time.isVisible = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onEnd = {
|
|
||||||
if (!Preferences.showClock) {
|
|
||||||
time.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}.start()
|
|
||||||
|
|
||||||
ValueAnimator.ofInt(
|
|
||||||
preview.height,
|
|
||||||
160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(this@MainActivity) else 0
|
|
||||||
).apply {
|
|
||||||
duration = 500L
|
|
||||||
addUpdateListener {
|
|
||||||
val animatedValue = animatedValue as Int
|
|
||||||
val layoutParams = preview.layoutParams
|
|
||||||
layoutParams.height = animatedValue
|
|
||||||
preview.layoutParams = layoutParams
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
} else {
|
|
||||||
time.layoutParams = time.layoutParams.apply {
|
|
||||||
height = RelativeLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
}
|
|
||||||
time.measure(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap_container.setImageBitmap(bitmap)
|
|
||||||
widget_loader.animate().scaleX(0f).scaleY(0f).start()
|
|
||||||
widget.animate().alpha(1f).start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun subscribeUi(viewModel: MainViewModel) {
|
|
||||||
viewModel.showWallpaper.observe(this, Observer {
|
|
||||||
widget_bg.setImageDrawable(if (it) getCurrentWallpaper() else null)
|
|
||||||
})
|
|
||||||
|
|
||||||
logo.setOnClickListener {
|
|
||||||
// startActivity(Intent(this, SupportDevActivity::class.java))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (mAppWidgetId > 0) {
|
if (mainNavController?.currentDestination?.id == R.id.appMainFragment) {
|
||||||
addNewWidget()
|
if (settingsNavController?.navigateUp() == false) {
|
||||||
|
if (mAppWidgetId > 0) {
|
||||||
|
addNewWidget()
|
||||||
|
} else {
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewModel.fragmentScrollY.value = 0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setResult(Activity.RESULT_OK)
|
super.onBackPressed()
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,8 +92,8 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
|||||||
AppWidgetManager.INVALID_APPWIDGET_ID)
|
AppWidgetManager.INVALID_APPWIDGET_ID)
|
||||||
|
|
||||||
if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
|
if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
|
||||||
action_add_widget.visibility = View.VISIBLE
|
binding.actionAddWidget.visibility = View.VISIBLE
|
||||||
action_add_widget.setOnClickListener {
|
binding.actionAddWidget.setOnClickListener {
|
||||||
addNewWidget()
|
addNewWidget()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,30 +112,49 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
private fun requirePermission() {
|
||||||
super.onDestroy()
|
Dexter.withContext(this)
|
||||||
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
|
.withPermissions(
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||||
|
).withListener(object : MultiplePermissionsListener {
|
||||||
|
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||||
|
report?.let {
|
||||||
|
Preferences.showWallpaper = false
|
||||||
|
Preferences.showWallpaper = report.areAllPermissionsGranted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPermissionRationaleShouldBeShown(
|
||||||
|
permissions: MutableList<PermissionRequest>?,
|
||||||
|
token: PermissionToken?
|
||||||
|
) {
|
||||||
|
// Remember to invoke this method when the custom rationale is closed
|
||||||
|
// or just by default if you don't want to use any custom rationale.
|
||||||
|
token?.cancelPermissionRequest()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.check()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
|
override fun onResume() {
|
||||||
updateUI()
|
super.onResume()
|
||||||
MainWidget.updateWidget(this)
|
|
||||||
|
if (Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
|
||||||
|
Preferences.showEvents = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
super.onStart()
|
super.onStart()
|
||||||
EventBus.getDefault().register(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
EventBus.getDefault().unregister(this)
|
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateUiMessageEvent
|
override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) {
|
||||||
|
MainWidget.updateWidget(this)
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
||||||
fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
|
|
||||||
updateUI()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityMainBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
class SplashActivity: AppCompatActivity() {
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
lifecycleScope.launchWhenResumed {
|
||||||
|
delay(1000)
|
||||||
|
|
||||||
|
if (!this@SplashActivity.isDestroyed) {
|
||||||
|
startActivity(Intent(this@SplashActivity, MainActivity::class.java))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,93 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.activities
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.location.Address
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.android.billingclient.api.*
|
|
||||||
import com.android.billingclient.api.BillingClient.BillingResponseCode.OK
|
|
||||||
import com.android.billingclient.api.BillingClient.BillingResponseCode.USER_CANCELED
|
|
||||||
import com.chibatching.kotpref.bulk
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.ActivitySupportDevBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.SupportDevViewModel
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.toast
|
|
||||||
import kotlinx.android.synthetic.main.activity_support_dev.*
|
|
||||||
import net.idik.lib.slimadapter.SlimAdapter
|
|
||||||
|
|
||||||
class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
|
|
||||||
|
|
||||||
private lateinit var viewModel: SupportDevViewModel
|
|
||||||
private lateinit var adapter: SlimAdapter
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this).get(SupportDevViewModel::class.java)
|
|
||||||
viewModel.billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build()
|
|
||||||
DataBindingUtil.setContentView<ActivitySupportDevBinding>(this, R.layout.activity_support_dev)
|
|
||||||
|
|
||||||
|
|
||||||
list_view.setHasFixedSize(true)
|
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
|
||||||
list_view.layoutManager = mLayoutManager
|
|
||||||
|
|
||||||
adapter = SlimAdapter.create()
|
|
||||||
adapter
|
|
||||||
.register<SkuDetails>(R.layout.inapp_product_layout) { item, injector ->
|
|
||||||
injector
|
|
||||||
.text(R.id.product_title, item.title.replace("(Another Widget)", ""))
|
|
||||||
.text(R.id.product_price, item.price)
|
|
||||||
.clicked(R.id.item) {
|
|
||||||
viewModel.purchase(this, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.attachTo(list_view)
|
|
||||||
|
|
||||||
viewModel.openConnection()
|
|
||||||
subscribeUi(viewModel)
|
|
||||||
|
|
||||||
action_back.setOnClickListener {
|
|
||||||
onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun subscribeUi(viewModel: SupportDevViewModel) {
|
|
||||||
viewModel.products.observe(this, Observer {
|
|
||||||
if (it.isNotEmpty()) {
|
|
||||||
loader.isVisible = false
|
|
||||||
}
|
|
||||||
adapter.updateData(it.sortedWith(compareBy(SkuDetails::getPriceAmountMicros)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
|
|
||||||
if (billingResult.responseCode == OK && purchases != null) {
|
|
||||||
for (purchase in purchases) {
|
|
||||||
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
|
|
||||||
viewModel.handlePurchase(purchase)
|
|
||||||
toast(getString(R.string.thanks))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (billingResult.responseCode == USER_CANCELED) {
|
|
||||||
// DO nothing
|
|
||||||
} else {
|
|
||||||
toast(getString(R.string.error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onDestroy() {
|
|
||||||
viewModel.closeConnection()
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.activities
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Html
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
|
||||||
import kotlinx.android.synthetic.main.activity_weather_provider.*
|
|
||||||
|
|
||||||
class WeatherProviderActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.activity_weather_provider)
|
|
||||||
|
|
||||||
action_back.setOnClickListener {
|
|
||||||
onBackPressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_save.setOnClickListener {
|
|
||||||
Preferences.weatherProviderApi = api_key.editText?.text.toString()
|
|
||||||
setResult(Activity.RESULT_OK)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_open_provider.setOnClickListener {
|
|
||||||
openURI("https://home.openweathermap.org/users/sign_up")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
last_info.text = Html.fromHtml(getString(R.string.api_key_info_all_set), Html.FROM_HTML_MODE_LEGACY)
|
|
||||||
} else {
|
|
||||||
last_info.text = Html.fromHtml(getString(R.string.api_key_info_all_set))
|
|
||||||
}
|
|
||||||
api_key.editText?.setText(Preferences.weatherProviderApi)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.settings
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityIntegrationsBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.settings.IntegrationsViewModel
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
|
||||||
|
class IntegrationsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: IntegrationsViewModel
|
||||||
|
private lateinit var binding: ActivityIntegrationsBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(IntegrationsViewModel::class.java)
|
||||||
|
binding = ActivityIntegrationsBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<String>(R.layout.application_info_layout) { _, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, getString(R.string.default_name))
|
||||||
|
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityIntegrationsBinding, viewModel: IntegrationsViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityAppNotificationsFilterBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.AppNotificationsViewModel
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class AppNotificationsFilterActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: AppNotificationsViewModel
|
||||||
|
private lateinit var binding: ActivityAppNotificationsFilterBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(AppNotificationsViewModel::class.java)
|
||||||
|
binding = ActivityAppNotificationsFilterBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<ResolveInfo>(R.layout.application_info_layout) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, item.loadLabel(viewModel.pm))
|
||||||
|
.with<ImageView>(R.id.icon) {
|
||||||
|
Glide
|
||||||
|
.with(this)
|
||||||
|
.load(item.loadIcon(viewModel.pm))
|
||||||
|
.centerCrop()
|
||||||
|
.into(it)
|
||||||
|
}
|
||||||
|
.visible(R.id.checkBox)
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
toggleApp(item)
|
||||||
|
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||||
|
}
|
||||||
|
.clicked(R.id.checkBox) {
|
||||||
|
toggleApp(item)
|
||||||
|
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||||
|
}
|
||||||
|
.checked(R.id.checkBox, ActiveNotificationsHelper.isAppAccepted(item.activityInfo.packageName))
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.search.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var filterJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityAppNotificationsFilterBinding, viewModel: AppNotificationsViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.appList.observe(this, Observer {
|
||||||
|
updateList(list = it)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.searchInput.observe(this, Observer { search ->
|
||||||
|
updateList(search = search)
|
||||||
|
binding.clearSearch.isVisible = search.isNotBlank()
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.appNotificationsFilter.observe(this, {
|
||||||
|
updateList()
|
||||||
|
binding.clearSelection.isVisible = Preferences.appNotificationsFilter != ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
filterJob?.cancel()
|
||||||
|
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if (list != null && list.isNotEmpty()) {
|
||||||
|
delay(200)
|
||||||
|
val filteredList: List<ResolveInfo> = if (search == null || search == "") {
|
||||||
|
list
|
||||||
|
} else {
|
||||||
|
list.filter {
|
||||||
|
it.loadLabel(viewModel.pm).contains(search, true)
|
||||||
|
}
|
||||||
|
}.sortedWith { app1, app2 ->
|
||||||
|
if (ActiveNotificationsHelper.isAppAccepted(app1.activityInfo.packageName) && ActiveNotificationsHelper.isAppAccepted(app2.activityInfo.packageName)) {
|
||||||
|
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||||
|
} else if (ActiveNotificationsHelper.isAppAccepted(app1.activityInfo.packageName)) {
|
||||||
|
-1
|
||||||
|
} else if (ActiveNotificationsHelper.isAppAccepted(app2.activityInfo.packageName)) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
adapter.updateData(filteredList)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSearch.setOnClickListener {
|
||||||
|
viewModel.searchInput.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSelection.setOnClickListener {
|
||||||
|
Preferences.appNotificationsFilter = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleApp(app: ResolveInfo) {
|
||||||
|
ActiveNotificationsHelper.toggleAppFilter(app.activityInfo.packageName)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,213 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.ChooseApplicationViewModel
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapterEx
|
||||||
|
|
||||||
|
|
||||||
|
class ChooseApplicationActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: ChooseApplicationViewModel
|
||||||
|
private lateinit var binding: ActivityChooseApplicationBinding
|
||||||
|
|
||||||
|
private var selectedPackage: String? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
selectedPackage = intent.extras?.getString(Constants.RESULT_APP_PACKAGE)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(ChooseApplicationViewModel::class.java)
|
||||||
|
binding = ActivityChooseApplicationBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapterEx.create()
|
||||||
|
adapter
|
||||||
|
.register<String>(R.layout.application_info_layout) { item, injector ->
|
||||||
|
when (item) {
|
||||||
|
IntentHelper.DO_NOTHING_OPTION -> {
|
||||||
|
injector
|
||||||
|
.text(R.id.text, getString(R.string.gestures_do_nothing))
|
||||||
|
.image(R.id.icon, R.drawable.round_no_cell_24)
|
||||||
|
.with<ImageView>(R.id.icon) {
|
||||||
|
it.scaleX = 0.8f
|
||||||
|
it.scaleY = 0.8f
|
||||||
|
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
|
||||||
|
}
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_NAME, IntentHelper.DO_NOTHING_OPTION)
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, IntentHelper.DO_NOTHING_OPTION)
|
||||||
|
setResult(Activity.RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.with<MaterialCardView>(R.id.item) {
|
||||||
|
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DO_NOTHING_OPTION) R.color.colorAccent else R.color.cardBorder)
|
||||||
|
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DO_NOTHING_OPTION) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IntentHelper.REFRESH_WIDGET_OPTION -> {
|
||||||
|
injector
|
||||||
|
.text(R.id.text, getString(R.string.action_refresh_widget))
|
||||||
|
.image(R.id.icon, R.drawable.round_refresh)
|
||||||
|
.with<ImageView>(R.id.icon) {
|
||||||
|
it.scaleX = 0.8f
|
||||||
|
it.scaleY = 0.8f
|
||||||
|
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
|
||||||
|
}
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_NAME, IntentHelper.REFRESH_WIDGET_OPTION)
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, IntentHelper.REFRESH_WIDGET_OPTION)
|
||||||
|
setResult(Activity.RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.with<MaterialCardView>(R.id.item) {
|
||||||
|
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == IntentHelper.REFRESH_WIDGET_OPTION) R.color.colorAccent else R.color.cardBorder)
|
||||||
|
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == IntentHelper.REFRESH_WIDGET_OPTION) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
injector
|
||||||
|
.text(R.id.text, getString(R.string.default_name))
|
||||||
|
.image(R.id.icon, R.drawable.round_add_to_home_screen_24)
|
||||||
|
.with<ImageView>(R.id.icon) {
|
||||||
|
it.scaleX = 0.8f
|
||||||
|
it.scaleY = 0.8f
|
||||||
|
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
|
||||||
|
}
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_NAME, IntentHelper.DEFAULT_OPTION)
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, IntentHelper.DEFAULT_OPTION)
|
||||||
|
setResult(Activity.RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.with<MaterialCardView>(R.id.item) {
|
||||||
|
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DEFAULT_OPTION) R.color.colorAccent else R.color.cardBorder)
|
||||||
|
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DEFAULT_OPTION) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.register<ResolveInfo>(R.layout.application_info_layout) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, item.loadLabel(viewModel.pm))
|
||||||
|
.with<ImageView>(R.id.icon) {
|
||||||
|
Glide
|
||||||
|
.with(this)
|
||||||
|
.load(item.loadIcon(viewModel.pm))
|
||||||
|
.centerCrop()
|
||||||
|
.into(it)
|
||||||
|
}
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
saveApp(item)
|
||||||
|
}
|
||||||
|
.with<MaterialCardView>(R.id.item) {
|
||||||
|
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == item.activityInfo.packageName) R.color.colorAccent else R.color.cardBorder)
|
||||||
|
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == item.activityInfo.packageName) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.search.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var filterJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityChooseApplicationBinding, viewModel: ChooseApplicationViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.appList.observe(this) {
|
||||||
|
updateList(list = it)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.searchInput.observe(this) { search ->
|
||||||
|
updateList(search = search)
|
||||||
|
binding.clearSearch.isVisible = search.isNotBlank()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
filterJob?.cancel()
|
||||||
|
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if (list != null && list.isNotEmpty()) {
|
||||||
|
delay(200)
|
||||||
|
val filteredList: List<ResolveInfo> = if (search == null || search == "") {
|
||||||
|
list
|
||||||
|
} else {
|
||||||
|
list.filter {
|
||||||
|
it.loadLabel(viewModel.pm).contains(search, true)
|
||||||
|
}
|
||||||
|
}.sortedWith { app1, app2 ->
|
||||||
|
when (selectedPackage) {
|
||||||
|
app1.activityInfo.packageName -> {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
app2.activityInfo.packageName -> {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
adapter.updateData(listOf(IntentHelper.DO_NOTHING_OPTION, IntentHelper.DEFAULT_OPTION, IntentHelper.REFRESH_WIDGET_OPTION) + filteredList)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSearch.setOnClickListener {
|
||||||
|
viewModel.searchInput.value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveApp(app: ResolveInfo) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_NAME, app.loadLabel(viewModel.pm))
|
||||||
|
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, app.activityInfo.packageName)
|
||||||
|
setResult(Activity.RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,184 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.chibatching.kotpref.blockingBulk
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomDateBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomDateViewModel
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class CustomDateActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: CustomDateViewModel
|
||||||
|
private lateinit var binding: ActivityCustomDateBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(CustomDateViewModel::class.java)
|
||||||
|
binding = ActivityCustomDateBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<String>(R.layout.custom_date_example_item) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.custom_date_example_format, item)
|
||||||
|
.text(R.id.custom_date_example_value, SimpleDateFormat(item, Locale.getDefault()).format(
|
||||||
|
DATE.time))
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
adapter.updateData(
|
||||||
|
listOf(
|
||||||
|
"d", "dd", "EE", "EEEE", "MM", "MMM", "MMMM", "yy", "yyyy"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.dateFormat.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var formatJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityCustomDateBinding, viewModel: CustomDateViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.dateInput.observe(this, Observer { dateFormat ->
|
||||||
|
formatJob?.cancel()
|
||||||
|
formatJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(200)
|
||||||
|
var text = if (dateFormat != "") {
|
||||||
|
try {
|
||||||
|
SimpleDateFormat(dateFormat, Locale.getDefault()).format(DATE.time)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
ERROR_STRING
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ERROR_STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewModel.isDateCapitalize.value == true) {
|
||||||
|
text = text.getCapWordString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewModel.isDateUppercase.value == true) {
|
||||||
|
text = text.toUpperCase(Locale.getDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
binding.dateFormatValue.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.isDateCapitalize.observe(this, Observer {
|
||||||
|
viewModel.dateInput.value = viewModel.dateInput.value
|
||||||
|
updateCapitalizeUi()
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.isDateUppercase.observe(this, Observer {
|
||||||
|
viewModel.dateInput.value = viewModel.dateInput.value
|
||||||
|
updateCapitalizeUi()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCapitalizeUi() {
|
||||||
|
when {
|
||||||
|
viewModel.isDateUppercase.value == true -> {
|
||||||
|
binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
|
||||||
|
binding.actionCapitalize.alpha = 1f
|
||||||
|
}
|
||||||
|
viewModel.isDateCapitalize.value == true -> {
|
||||||
|
binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.ic_capitalize))
|
||||||
|
binding.actionCapitalize.alpha = 1f
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
|
||||||
|
binding.actionCapitalize.alpha = 0.3f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.actionCapitalize.setOnClickListener {
|
||||||
|
when {
|
||||||
|
viewModel.isDateUppercase.value == true -> {
|
||||||
|
viewModel.isDateCapitalize.value = false
|
||||||
|
viewModel.isDateUppercase.value = false
|
||||||
|
}
|
||||||
|
viewModel.isDateCapitalize.value == true -> {
|
||||||
|
viewModel.isDateCapitalize.value = false
|
||||||
|
viewModel.isDateUppercase.value = true
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
viewModel.isDateCapitalize.value = true
|
||||||
|
viewModel.isDateUppercase.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.actionCapitalize.setOnLongClickListener {
|
||||||
|
toast(getString(R.string.action_capitalize_the_date))
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.actionDateFormatInfo.setOnClickListener {
|
||||||
|
openURI("https://developer.android.com/reference/java/text/SimpleDateFormat")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
Preferences.blockingBulk {
|
||||||
|
dateFormat = viewModel.dateInput.value ?: ""
|
||||||
|
isDateCapitalize = viewModel.isDateCapitalize.value ?: true
|
||||||
|
isDateUppercase = viewModel.isDateUppercase.value ?: false
|
||||||
|
}
|
||||||
|
com.tommasoberlose.anotherwidget.ui.widgets.MainWidget.updateWidget(this)
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ERROR_STRING = "--"
|
||||||
|
val DATE: Calendar = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.MONTH, 10)
|
||||||
|
set(Calendar.DAY_OF_MONTH, 1)
|
||||||
|
set(Calendar.YEAR, 1993)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,280 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.provider.FontRequest
|
||||||
|
import androidx.core.provider.FontsContractCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.chibatching.kotpref.blockingBulk
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.koolio.library.Font
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomFontBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomFontViewModel
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
import net.idik.lib.slimadapter.diff.DefaultDiffCallback
|
||||||
|
|
||||||
|
|
||||||
|
class CustomFontActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: CustomFontViewModel
|
||||||
|
private lateinit var binding: ActivityCustomFontBinding
|
||||||
|
private lateinit var handlerThread: HandlerThread
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java)
|
||||||
|
binding = ActivityCustomFontBinding.inflate(layoutInflater)
|
||||||
|
handlerThread = HandlerThread("listCustomFonts")
|
||||||
|
handlerThread.start()
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter.enableDiff(object: DefaultDiffCallback() {
|
||||||
|
override fun areItemsTheSame(oldItem: Any?, newItem: Any?): Boolean {
|
||||||
|
return oldItem is Font && newItem is Font && oldItem.fontFamily == newItem.fontFamily
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Any?, newItem: Any?): Boolean {
|
||||||
|
return oldItem is Font && newItem is Font && oldItem.fontFamily == newItem.fontFamily
|
||||||
|
}
|
||||||
|
})
|
||||||
|
adapter
|
||||||
|
.register<String>(R.layout.list_item) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, item)
|
||||||
|
.with<TextView>(R.id.text) {
|
||||||
|
val googleSans: Typeface? = androidx.core.content.res.ResourcesCompat.getFont(
|
||||||
|
this,
|
||||||
|
when (Preferences.customFontVariant) {
|
||||||
|
"100" -> R.font.google_sans_thin
|
||||||
|
"200" -> R.font.google_sans_light
|
||||||
|
"500" -> R.font.google_sans_medium
|
||||||
|
"700" -> R.font.google_sans_bold
|
||||||
|
"800" -> R.font.google_sans_black
|
||||||
|
else -> R.font.google_sans_regular
|
||||||
|
}
|
||||||
|
)
|
||||||
|
it.typeface = googleSans
|
||||||
|
}
|
||||||
|
|
||||||
|
injector.clicked(R.id.text) {
|
||||||
|
val dialog = BottomSheetMenu<String>(this, header = item)
|
||||||
|
listOf("100", "200", "regular", "500", "700", "800").forEachIndexed { _, s ->
|
||||||
|
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), s)
|
||||||
|
}
|
||||||
|
dialog.addOnSelectItemListener { value ->
|
||||||
|
saveGoogleSansFont(value)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.register<Font>(R.layout.list_item) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, item.fontFamily)
|
||||||
|
.with<TextView>(R.id.text) {
|
||||||
|
val request = FontRequest(
|
||||||
|
"com.google.android.gms.fonts",
|
||||||
|
"com.google.android.gms",
|
||||||
|
item.queryString,
|
||||||
|
R.array.com_google_android_gms_fonts_certs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Callback : FontsContractCompat.FontRequestCallback() {
|
||||||
|
var handler: Handler? = Handler(handlerThread.looper)
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
if (handler != null) {
|
||||||
|
handler!!.removeCallbacksAndMessages(null)
|
||||||
|
handler = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun finalize() {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTypefaceRetrieved(typeface: Typeface) {
|
||||||
|
if (it.tag == this) {
|
||||||
|
it.tag = null
|
||||||
|
it.typeface = typeface
|
||||||
|
it.setTextColor(getColor(R.color.colorPrimaryText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTypefaceRequestFailed(reason: Int) {
|
||||||
|
if (it.tag == this) {
|
||||||
|
it.tag = null
|
||||||
|
//it.text = item.fontFamily + " ($reason)"
|
||||||
|
it.setTextColor(getColor(R.color.errorColorText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(it.tag as Callback?)?.cancel()
|
||||||
|
val callback = Callback()
|
||||||
|
it.tag = callback
|
||||||
|
it.typeface = null
|
||||||
|
it.setTextColor(getColor(R.color.colorSecondaryText))
|
||||||
|
|
||||||
|
val mHandler = callback.handler!!
|
||||||
|
FontsContractCompat.requestFont(this, request, callback, mHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
injector.clicked(R.id.text) {
|
||||||
|
if ((it as TextView).typeface == null) return@clicked
|
||||||
|
val dialog = BottomSheetMenu<Int>(this, header = item.fontFamily)
|
||||||
|
if (item.fontVariants.isEmpty()) {
|
||||||
|
dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1)
|
||||||
|
} else {
|
||||||
|
item.fontVariants
|
||||||
|
.forEachIndexed { index, s ->
|
||||||
|
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.addOnSelectItemListener { value ->
|
||||||
|
saveFont(item, value)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.search.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
handlerThread.quit()
|
||||||
|
filterJob?.cancel()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var filterJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityCustomFontBinding, viewModel: CustomFontViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.fontList.observe(this, Observer {
|
||||||
|
updateList(list = it)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.searchInput.observe(this, Observer { search ->
|
||||||
|
updateList(search = search)
|
||||||
|
binding.clearSearch.isVisible = search.isNotBlank()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateList(
|
||||||
|
list: ArrayList<Font>? = viewModel.fontList.value,
|
||||||
|
search: String? = viewModel.searchInput.value
|
||||||
|
) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
filterJob?.cancel()
|
||||||
|
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if (list != null && list.isNotEmpty()) {
|
||||||
|
delay(200)
|
||||||
|
val filteredList: List<Any> = if (search == null || search == "") {
|
||||||
|
listOf(getString(R.string.custom_font_subtitle_1)) + list.distinctBy { it.fontFamily }
|
||||||
|
} else {
|
||||||
|
(listOf(getString(R.string.custom_font_subtitle_1)) + list.distinctBy { it.fontFamily }).filter {
|
||||||
|
when (it) {
|
||||||
|
is Font -> {
|
||||||
|
it.fontFamily.contains(search, true)
|
||||||
|
}
|
||||||
|
is String -> {
|
||||||
|
it.contains(search, ignoreCase = true)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.sortedWith { el1, el2 ->
|
||||||
|
if (el1 is Font && el2 is Font) {
|
||||||
|
el1.fontFamily.compareTo(el2.fontFamily)
|
||||||
|
} else if (el1 is Font && el2 is String) {
|
||||||
|
el1.fontFamily.compareTo(el2)
|
||||||
|
} else if (el1 is String && el2 is Font) {
|
||||||
|
el1.compareTo(el2.fontFamily)
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
adapter.updateData(filteredList)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delay(200)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
adapter.updateData(listOf(getString(R.string.custom_font_subtitle_1)).filter {
|
||||||
|
it.contains(search ?: "", ignoreCase = true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSearch.setOnClickListener {
|
||||||
|
viewModel.searchInput.value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveFont(font: Font, variantPos: Int? = null) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
Preferences.blockingBulk {
|
||||||
|
customFont = Constants.CUSTOM_FONT_DOWNLOADED
|
||||||
|
customFontName = font.fontFamily
|
||||||
|
customFontFile = if (variantPos != null && variantPos > -1) font.getQueryString(variantPos) else font.queryString
|
||||||
|
customFontVariant = if (variantPos != null && variantPos > -1) font.fontVariants[variantPos] else "regular"
|
||||||
|
}
|
||||||
|
setResult(Activity.RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveGoogleSansFont(variant: String) {
|
||||||
|
val resultIntent = Intent()
|
||||||
|
Preferences.blockingBulk {
|
||||||
|
customFont = Constants.CUSTOM_FONT_GOOGLE_SANS
|
||||||
|
customFontName = ""
|
||||||
|
customFontFile = ""
|
||||||
|
customFontVariant = variant
|
||||||
|
}
|
||||||
|
setResult(Activity.RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
@ -1,82 +1,75 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.activities
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.location.Address
|
import android.location.Address
|
||||||
import android.location.Geocoder
|
import android.location.Geocoder
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import com.tommasoberlose.anotherwidget.R
|
import com.tommasoberlose.anotherwidget.R
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.Window
|
|
||||||
import android.widget.AdapterView
|
|
||||||
import android.widget.ArrayAdapter
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.chibatching.kotpref.bulk
|
import com.chibatching.kotpref.bulk
|
||||||
import com.google.android.material.transition.MaterialFadeThrough
|
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
|
||||||
import com.karumi.dexter.Dexter
|
import com.karumi.dexter.Dexter
|
||||||
import com.karumi.dexter.MultiplePermissionsReport
|
import com.karumi.dexter.MultiplePermissionsReport
|
||||||
import com.karumi.dexter.PermissionToken
|
import com.karumi.dexter.PermissionToken
|
||||||
import com.karumi.dexter.listener.PermissionRequest
|
import com.karumi.dexter.listener.PermissionRequest
|
||||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||||
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomLocationBinding
|
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomLocationBinding
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomLocationViewModel
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomLocationViewModel
|
|
||||||
import kotlinx.android.synthetic.main.activity_custom_location.*
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.idik.lib.slimadapter.SlimAdapter
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
|
||||||
|
|
||||||
class CustomLocationActivity : AppCompatActivity() {
|
class CustomLocationActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var adapter: SlimAdapter
|
private lateinit var adapter: SlimAdapter
|
||||||
private lateinit var viewModel: CustomLocationViewModel
|
private lateinit var viewModel: CustomLocationViewModel
|
||||||
|
private lateinit var binding: ActivityCustomLocationBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this).get(CustomLocationViewModel::class.java)
|
viewModel = ViewModelProvider(this).get(CustomLocationViewModel::class.java)
|
||||||
val binding = DataBindingUtil.setContentView<ActivityCustomLocationBinding>(this, R.layout.activity_custom_location)
|
binding = ActivityCustomLocationBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
|
||||||
list_view.setHasFixedSize(true)
|
binding.listView.setHasFixedSize(true)
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
list_view.layoutManager = mLayoutManager
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
adapter = SlimAdapter.create()
|
adapter = SlimAdapter.create()
|
||||||
adapter
|
adapter
|
||||||
.register<String>(R.layout.custom_location_item) { _, injector ->
|
.register<String>(R.layout.custom_location_item) { _, injector ->
|
||||||
injector
|
injector.text(R.id.text, getString(R.string.custom_location_gps))
|
||||||
.text(R.id.text, getString(R.string.custom_location_gps))
|
injector.clicked(R.id.text) {
|
||||||
.clicked(R.id.text) {
|
Preferences.bulk {
|
||||||
requirePermission()
|
remove(Preferences::customLocationLat)
|
||||||
|
remove(Preferences::customLocationLon)
|
||||||
|
remove(Preferences::customLocationAdd)
|
||||||
}
|
}
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.register<Address>(R.layout.custom_location_item) { item, injector ->
|
.register<Address>(R.layout.custom_location_item) { item, injector ->
|
||||||
injector.text(R.id.text, item.getAddressLine(0))
|
injector.text(R.id.text, item.getAddressLine(0) ?: "")
|
||||||
injector.clicked(R.id.text) {
|
injector.clicked(R.id.item) {
|
||||||
Preferences.bulk {
|
Preferences.bulk {
|
||||||
customLocationLat = item.latitude.toString()
|
customLocationLat = item.latitude.toString()
|
||||||
customLocationLon = item.longitude.toString()
|
customLocationLon = item.longitude.toString()
|
||||||
customLocationAdd = item.getAddressLine(0)
|
customLocationAdd = item.getAddressLine(0) ?: ""
|
||||||
setResult(Activity.RESULT_OK)
|
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.attachTo(list_view)
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
|
||||||
viewModel.addresses.observe(this, Observer {
|
viewModel.addresses.observe(this, Observer {
|
||||||
@ -86,20 +79,24 @@ class CustomLocationActivity : AppCompatActivity() {
|
|||||||
setupListener()
|
setupListener()
|
||||||
subscribeUi(binding, viewModel)
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
location.requestFocus()
|
binding.location.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var searchJob: Job? = null
|
private var searchJob: Job? = null
|
||||||
|
|
||||||
private fun subscribeUi(binding: ActivityCustomLocationBinding, viewModel: CustomLocationViewModel) {
|
private fun subscribeUi(binding: ActivityCustomLocationBinding, viewModel: CustomLocationViewModel) {
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
viewModel.addresses.observe(this, Observer {
|
viewModel.addresses.observe(this, Observer {
|
||||||
adapter.updateData(listOf("Default") + it)
|
adapter.updateData(listOf("Default") + it)
|
||||||
loader.visibility = View.INVISIBLE
|
binding.loader.visibility = View.INVISIBLE
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.locationInput.observe(this, Observer { location ->
|
viewModel.locationInput.observe(this, Observer { location ->
|
||||||
loader.visibility = View.VISIBLE
|
binding.loader.visibility = View.VISIBLE
|
||||||
searchJob?.cancel()
|
searchJob?.cancel()
|
||||||
searchJob = lifecycleScope.launch(Dispatchers.IO) {
|
searchJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
delay(200)
|
delay(200)
|
||||||
@ -115,46 +112,21 @@ class CustomLocationActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
viewModel.addresses.value = list
|
viewModel.addresses.value = list
|
||||||
loader.visibility = View.INVISIBLE
|
binding.loader.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
binding.clearSearch.isVisible = location.isNotBlank()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requirePermission() {
|
|
||||||
Dexter.withContext(this)
|
|
||||||
.withPermissions(
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION
|
|
||||||
).withListener(object: MultiplePermissionsListener {
|
|
||||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
|
||||||
report?.let {
|
|
||||||
if (report.areAllPermissionsGranted()){
|
|
||||||
Preferences.bulk {
|
|
||||||
remove(Preferences::customLocationLat)
|
|
||||||
remove(Preferences::customLocationLon)
|
|
||||||
remove(Preferences::customLocationAdd)
|
|
||||||
}
|
|
||||||
setResult(Activity.RESULT_OK)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onPermissionRationaleShouldBeShown(
|
|
||||||
permissions: MutableList<PermissionRequest>?,
|
|
||||||
token: PermissionToken?
|
|
||||||
) {
|
|
||||||
// Remember to invoke this method when the custom rationale is closed
|
|
||||||
// or just by default if you don't want to use any custom rationale.
|
|
||||||
token?.continuePermissionRequest()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.check()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
private fun setupListener() {
|
||||||
action_back.setOnClickListener {
|
binding.actionBack.setOnClickListener {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.clearSearch.setOnClickListener {
|
||||||
|
viewModel.locationInput.value = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.chibatching.kotpref.blockingBulk
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityMediaInfoFormatBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.MediaInfoFormatViewModel
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MediaInfoFormatActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: MediaInfoFormatViewModel
|
||||||
|
private lateinit var binding: ActivityMediaInfoFormatBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(MediaInfoFormatViewModel::class.java)
|
||||||
|
binding = ActivityMediaInfoFormatBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<String>(R.layout.custom_date_example_item) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.custom_date_example_format, item)
|
||||||
|
.text(
|
||||||
|
R.id.custom_date_example_value, MediaPlayerHelper.getMediaInfo(item, EXAMPLE_TITLE, EXAMPLE_ARTIST, EXAMPLE_ALBUM))
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
adapter.updateData(
|
||||||
|
listOf(
|
||||||
|
MediaPlayerHelper.MEDIA_INFO_TITLE, MediaPlayerHelper.MEDIA_INFO_ARTIST, MediaPlayerHelper.MEDIA_INFO_ALBUM
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.mediaInfoFormatInput.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var formatJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityMediaInfoFormatBinding, viewModel: MediaInfoFormatViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.mediaInfoFormatInput.observe(this) { mediaInfoFormatInput ->
|
||||||
|
formatJob?.cancel()
|
||||||
|
formatJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(200)
|
||||||
|
val text = MediaPlayerHelper.getMediaInfo(mediaInfoFormatInput, EXAMPLE_TITLE, EXAMPLE_ARTIST, EXAMPLE_ALBUM)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
binding.mediaInfoFormatInputValue.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
Preferences.blockingBulk {
|
||||||
|
mediaInfoFormat = viewModel.mediaInfoFormatInput.value ?: ""
|
||||||
|
}
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXAMPLE_TITLE = "Thunderstruck"
|
||||||
|
const val EXAMPLE_ARTIST = "AC/DC"
|
||||||
|
const val EXAMPLE_ALBUM = "The Razors Edge"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityMusicPlayersFilterBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.MusicPlayersFilterViewModel
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class MusicPlayersFilterActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: MusicPlayersFilterViewModel
|
||||||
|
private lateinit var binding: ActivityMusicPlayersFilterBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(MusicPlayersFilterViewModel::class.java)
|
||||||
|
binding = ActivityMusicPlayersFilterBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<ResolveInfo>(R.layout.application_info_layout) { item, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, item.loadLabel(viewModel.pm))
|
||||||
|
.with<ImageView>(R.id.icon) {
|
||||||
|
Glide
|
||||||
|
.with(this)
|
||||||
|
.load(item.loadIcon(viewModel.pm))
|
||||||
|
.centerCrop()
|
||||||
|
.into(it)
|
||||||
|
}
|
||||||
|
.visible(R.id.checkBox)
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
toggleApp(item)
|
||||||
|
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||||
|
}
|
||||||
|
.clicked(R.id.checkBox) {
|
||||||
|
toggleApp(item)
|
||||||
|
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||||
|
}
|
||||||
|
.checked(R.id.checkBox, MediaPlayerHelper.isMusicPlayerAccepted(item.activityInfo.packageName))
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.search.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var filterJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityMusicPlayersFilterBinding, viewModel: MusicPlayersFilterViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.appList.observe(this) {
|
||||||
|
updateList(list = it)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.searchInput.observe(this) { search ->
|
||||||
|
updateList(search = search)
|
||||||
|
binding.clearSearch.isVisible = search.isNotBlank()
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.musicPlayersFilter.observe(this) {
|
||||||
|
updateList()
|
||||||
|
binding.clearSelection.isVisible = Preferences.musicPlayersFilter != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
filterJob?.cancel()
|
||||||
|
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if (list != null && list.isNotEmpty()) {
|
||||||
|
delay(200)
|
||||||
|
val filteredList: List<ResolveInfo> = if (search == null || search == "") {
|
||||||
|
list
|
||||||
|
} else {
|
||||||
|
list.filter {
|
||||||
|
it.loadLabel(viewModel.pm).contains(search, true)
|
||||||
|
}
|
||||||
|
}.sortedWith { app1, app2 ->
|
||||||
|
if (MediaPlayerHelper.isMusicPlayerAccepted(app1.activityInfo.packageName) && MediaPlayerHelper.isMusicPlayerAccepted(app2.activityInfo.packageName)) {
|
||||||
|
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||||
|
} else if (MediaPlayerHelper.isMusicPlayerAccepted(app1.activityInfo.packageName)) {
|
||||||
|
-1
|
||||||
|
} else if (MediaPlayerHelper.isMusicPlayerAccepted(app2.activityInfo.packageName)) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
adapter.updateData(filteredList)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSearch.setOnClickListener {
|
||||||
|
viewModel.searchInput.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSelection.setOnClickListener {
|
||||||
|
Preferences.musicPlayersFilter = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleApp(app: ResolveInfo) {
|
||||||
|
MediaPlayerHelper.toggleMusicPlayerFilter(app.activityInfo.packageName)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Activity
|
||||||
|
import android.location.Address
|
||||||
|
import android.location.Geocoder
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.chibatching.kotpref.bulk
|
||||||
|
import com.karumi.dexter.Dexter
|
||||||
|
import com.karumi.dexter.MultiplePermissionsReport
|
||||||
|
import com.karumi.dexter.PermissionToken
|
||||||
|
import com.karumi.dexter.listener.PermissionRequest
|
||||||
|
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityTimeZoneSelectorBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.network.TimeZonesApi
|
||||||
|
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.TimeZoneSelectorViewModel
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||||
|
import com.tommasoberlose.anotherwidget.utils.toast
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
|
||||||
|
class TimeZoneSelectorActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: TimeZoneSelectorViewModel
|
||||||
|
private lateinit var binding: ActivityTimeZoneSelectorBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(TimeZoneSelectorViewModel::class.java)
|
||||||
|
binding = ActivityTimeZoneSelectorBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.geonameCredits.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<String>(R.layout.custom_location_item) { _, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, getString(R.string.no_time_zone_label))
|
||||||
|
.clicked(R.id.text) {
|
||||||
|
Preferences.bulk {
|
||||||
|
altTimezoneId = ""
|
||||||
|
altTimezoneLabel = ""
|
||||||
|
}
|
||||||
|
MainWidget.updateWidget(this@TimeZoneSelectorActivity)
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.register<Address>(R.layout.custom_location_item) { item, injector ->
|
||||||
|
injector.text(R.id.text, item.getAddressLine(0))
|
||||||
|
injector.clicked(R.id.item) {
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val networkApi = TimeZonesApi(this@TimeZoneSelectorActivity)
|
||||||
|
val id = networkApi.getTimeZone(item.latitude.toString(), item.longitude.toString())
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
Preferences.bulk {
|
||||||
|
altTimezoneId = id
|
||||||
|
altTimezoneLabel = try {
|
||||||
|
item.locality
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
item.getAddressLine(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MainWidget.updateWidget(this@TimeZoneSelectorActivity)
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
toast(getString(R.string.time_zone_search_error_message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attachTo(binding.listView)
|
||||||
|
|
||||||
|
|
||||||
|
viewModel.addresses.observe(this, Observer {
|
||||||
|
adapter.updateData(listOf("Default") + it)
|
||||||
|
})
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(binding, viewModel)
|
||||||
|
|
||||||
|
binding.location.requestFocus()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var searchJob: Job? = null
|
||||||
|
|
||||||
|
private fun subscribeUi(binding: ActivityTimeZoneSelectorBinding, viewModel: TimeZoneSelectorViewModel) {
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.addresses.observe(this, Observer {
|
||||||
|
adapter.updateData(listOf("Default") + it)
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.locationInput.observe(this, Observer { location ->
|
||||||
|
binding.loader.visibility = View.VISIBLE
|
||||||
|
searchJob?.cancel()
|
||||||
|
searchJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
delay(200)
|
||||||
|
val list = if (location == null || location == "") {
|
||||||
|
viewModel.addresses.value!!
|
||||||
|
} else {
|
||||||
|
val coder = Geocoder(this@TimeZoneSelectorActivity)
|
||||||
|
try {
|
||||||
|
coder.getFromLocationName(location, 10) as ArrayList<Address>
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
emptyList<Address>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
viewModel.addresses.value = list
|
||||||
|
binding.loader.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
binding.clearSearch.isVisible = location.isNotBlank()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.clearSearch.setOnClickListener {
|
||||||
|
viewModel.locationInput.value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
package com.tommasoberlose.anotherwidget.ui.activities.tabs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.tommasoberlose.anotherwidget.R
|
||||||
|
import com.tommasoberlose.anotherwidget.components.BottomSheetWeatherProviderSettings
|
||||||
|
import com.tommasoberlose.anotherwidget.databinding.ActivityWeatherProviderBinding
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Constants
|
||||||
|
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||||
|
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||||
|
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.WeatherProviderViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import net.idik.lib.slimadapter.SlimAdapter
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
|
|
||||||
|
class WeatherProviderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var adapter: SlimAdapter
|
||||||
|
private lateinit var viewModel: WeatherProviderViewModel
|
||||||
|
private lateinit var binding: ActivityWeatherProviderBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this).get(WeatherProviderViewModel::class.java)
|
||||||
|
binding = ActivityWeatherProviderBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.listView.setHasFixedSize(true)
|
||||||
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
binding.listView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
|
adapter = SlimAdapter.create()
|
||||||
|
adapter
|
||||||
|
.register<Constants.WeatherProvider>(R.layout.weather_provider_list_item) { provider, injector ->
|
||||||
|
injector
|
||||||
|
.text(R.id.text, WeatherHelper.getProviderName(this, provider))
|
||||||
|
.clicked(R.id.item) {
|
||||||
|
if (Preferences.weatherProvider != provider.rawValue) {
|
||||||
|
Preferences.weatherProviderError = "-"
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
val oldValue = Preferences.weatherProvider
|
||||||
|
Preferences.weatherProvider = provider.rawValue
|
||||||
|
updateListItem(oldValue)
|
||||||
|
updateListItem()
|
||||||
|
binding.loader.isVisible = true
|
||||||
|
|
||||||
|
WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
|
||||||
|
}
|
||||||
|
.clicked(R.id.radioButton) {
|
||||||
|
if (Preferences.weatherProvider != provider.rawValue) {
|
||||||
|
Preferences.weatherProviderError = "-"
|
||||||
|
Preferences.weatherProviderLocationError = ""
|
||||||
|
}
|
||||||
|
val oldValue = Preferences.weatherProvider
|
||||||
|
Preferences.weatherProvider = provider.rawValue
|
||||||
|
updateListItem(oldValue)
|
||||||
|
updateListItem()
|
||||||
|
binding.loader.isVisible = true
|
||||||
|
|
||||||
|
WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
|
||||||
|
}
|
||||||
|
.checked(R.id.radioButton, provider.rawValue == Preferences.weatherProvider)
|
||||||
|
.with<TextView>(R.id.text2) {
|
||||||
|
if (WeatherHelper.isKeyRequired(provider)) {
|
||||||
|
it.text = getString(R.string.api_key_required_message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider == Constants.WeatherProvider.WEATHER_GOV) {
|
||||||
|
it.text = getString(R.string.us_only_message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider == Constants.WeatherProvider.YR) {
|
||||||
|
it.text = getString(R.string.celsius_only_message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.clicked(R.id.action_configure) {
|
||||||
|
BottomSheetWeatherProviderSettings(this) {
|
||||||
|
binding.loader.isVisible = true
|
||||||
|
WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
.visibility(R.id.action_configure, if (/*WeatherHelper.isKeyRequired(provider) && */provider.rawValue == Preferences.weatherProvider) View.VISIBLE else View.GONE)
|
||||||
|
.with<TextView>(R.id.provider_error) {
|
||||||
|
if (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") {
|
||||||
|
it.text = Preferences.weatherProviderError
|
||||||
|
it.isVisible = provider.rawValue == Preferences.weatherProvider
|
||||||
|
} else if (Preferences.weatherProviderLocationError != "") {
|
||||||
|
it.text = Preferences.weatherProviderLocationError
|
||||||
|
it.isVisible = provider.rawValue == Preferences.weatherProvider
|
||||||
|
} else {
|
||||||
|
it.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.image(R.id.action_configure, ContextCompat.getDrawable(this, if (WeatherHelper.isKeyRequired(provider)) R.drawable.round_settings_24 else R.drawable.outline_info_24))
|
||||||
|
}.attachTo(binding.listView)
|
||||||
|
|
||||||
|
adapter.updateData(
|
||||||
|
Constants.WeatherProvider.values().asList()
|
||||||
|
)
|
||||||
|
|
||||||
|
setupListener()
|
||||||
|
subscribeUi(viewModel)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun subscribeUi(viewModel: WeatherProviderViewModel) {
|
||||||
|
viewModel.weatherProviderError.observe(this) {
|
||||||
|
binding.listView.postDelayed({ updateListItem() }, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.weatherProviderLocationError.observe(this) {
|
||||||
|
binding.listView.postDelayed({ updateListItem() }, 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateListItem(provider: Int = Preferences.weatherProvider) {
|
||||||
|
(adapter.data).forEachIndexed { index, item ->
|
||||||
|
if (item is Constants.WeatherProvider && item.rawValue == provider) {
|
||||||
|
adapter.notifyItemChanged(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupListener() {
|
||||||
|
binding.actionBack.setOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
EventBus.getDefault().register(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
EventBus.getDefault().unregister(this)
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onMessageEvent(@Suppress("UNUSED_PARAMETER") ignore: MainFragment.UpdateUiMessageEvent?) {
|
||||||
|
binding.loader.isVisible = Preferences.weatherProviderError == "-"
|
||||||
|
if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") {
|
||||||
|
Snackbar.make(binding.listView, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ package com.tommasoberlose.anotherwidget.ui.adapters
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import com.tommasoberlose.anotherwidget.ui.fragments.*
|
import com.tommasoberlose.anotherwidget.ui.fragments.tabs.*
|
||||||
|
|
||||||
class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
|
class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
|
||||||
FragmentStateAdapter(fragmentActivity) {
|
FragmentStateAdapter(fragmentActivity) {
|
||||||
@ -12,11 +12,11 @@ class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
|
|||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return when (position) {
|
return when (position) {
|
||||||
1 -> CalendarSettingsFragment.newInstance()
|
1 -> CalendarFragment.newInstance()
|
||||||
2 -> WeatherSettingsFragment.newInstance()
|
2 -> WeatherFragment.newInstance()
|
||||||
3 -> ClockSettingsFragment.newInstance()
|
3 -> ClockFragment.newInstance()
|
||||||
4 -> AdvancedSettingsFragment.newInstance()
|
4 -> GlanceTabFragment.newInstance()
|
||||||
else -> GeneralSettingsFragment.newInstance()
|
else -> LayoutFragment.newInstance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,191 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.fragments
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.karumi.dexter.Dexter
|
|
||||||
import com.karumi.dexter.MultiplePermissionsReport
|
|
||||||
import com.karumi.dexter.PermissionToken
|
|
||||||
import com.karumi.dexter.listener.PermissionRequest
|
|
||||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
|
||||||
import com.tommasoberlose.anotherwidget.BuildConfig
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.FragmentAdvancedSettingsBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.SupportDevActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
|
||||||
import kotlinx.android.synthetic.main.fragment_advanced_settings.*
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
|
|
||||||
class AdvancedSettingsFragment : Fragment() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun newInstance() = AdvancedSettingsFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var viewModel: MainViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
|
|
||||||
val binding = DataBindingUtil.inflate<FragmentAdvancedSettingsBinding>(inflater, R.layout.fragment_advanced_settings, container, false)
|
|
||||||
|
|
||||||
subscribeUi(viewModel)
|
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
|
|
||||||
setupListener()
|
|
||||||
|
|
||||||
app_version.text = "v%s".format(BuildConfig.VERSION_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun subscribeUi(
|
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
|
||||||
viewModel.darkThemePreference.observe(viewLifecycleOwner, Observer {
|
|
||||||
AppCompatDelegate.setDefaultNightMode(it)
|
|
||||||
theme.text = when (it) {
|
|
||||||
AppCompatDelegate.MODE_NIGHT_NO -> getString(R.string.settings_subtitle_dark_theme_light)
|
|
||||||
AppCompatDelegate.MODE_NIGHT_YES -> getString(R.string.settings_subtitle_dark_theme_dark)
|
|
||||||
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY -> getString(R.string.settings_subtitle_dark_theme_by_battery_saver)
|
|
||||||
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> getString(R.string.settings_subtitle_dark_theme_follow_system)
|
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer {
|
|
||||||
show_wallpaper_label.text = if (it && activity?.checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == true) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
|
||||||
action_change_theme.setOnClickListener {
|
|
||||||
maintainScrollPosition {
|
|
||||||
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_theme_title))
|
|
||||||
.setSelectedValue(Preferences.darkThemePreference)
|
|
||||||
.addItem(
|
|
||||||
getString(R.string.settings_subtitle_dark_theme_light),
|
|
||||||
AppCompatDelegate.MODE_NIGHT_NO
|
|
||||||
)
|
|
||||||
.addItem(
|
|
||||||
getString(R.string.settings_subtitle_dark_theme_dark),
|
|
||||||
AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
)
|
|
||||||
.addItem(
|
|
||||||
getString(R.string.settings_subtitle_dark_theme_default),
|
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM else AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
|
|
||||||
)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.darkThemePreference = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_wallpaper.setOnClickListener {
|
|
||||||
maintainScrollPosition {
|
|
||||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_title_show_wallpaper))
|
|
||||||
.setSelectedValue(Preferences.showWallpaper)
|
|
||||||
.addItem(
|
|
||||||
getString(R.string.settings_visible),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
.addItem(
|
|
||||||
getString(R.string.settings_not_visible),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
if (value) {
|
|
||||||
requirePermission()
|
|
||||||
} else {
|
|
||||||
Preferences.showWallpaper = value
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_translate.setOnClickListener {
|
|
||||||
activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml")
|
|
||||||
}
|
|
||||||
|
|
||||||
action_website.setOnClickListener {
|
|
||||||
activity?.openURI("http://tommasoberlose.com/")
|
|
||||||
}
|
|
||||||
|
|
||||||
action_feedback.setOnClickListener {
|
|
||||||
activity?.openURI("https://github.com/tommasoberlose/another-widget/issues")
|
|
||||||
}
|
|
||||||
|
|
||||||
action_help_dev.setOnClickListener {
|
|
||||||
startActivity(Intent(requireContext(), SupportDevActivity::class.java))
|
|
||||||
}
|
|
||||||
|
|
||||||
action_refresh_widget.setOnClickListener {
|
|
||||||
MainWidget.updateWidget(requireContext())
|
|
||||||
CalendarHelper.updateEventList(requireContext())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
|
||||||
val scrollPosition = scrollView.scrollY
|
|
||||||
callback.invoke()
|
|
||||||
lifecycleScope.launch {
|
|
||||||
delay(200)
|
|
||||||
scrollView.smoothScrollTo(0, scrollPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requirePermission() {
|
|
||||||
Dexter.withContext(requireContext())
|
|
||||||
.withPermissions(
|
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
|
||||||
).withListener(object: MultiplePermissionsListener {
|
|
||||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
|
||||||
report?.let {
|
|
||||||
Preferences.showWallpaper = report.areAllPermissionsGranted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onPermissionRationaleShouldBeShown(
|
|
||||||
permissions: MutableList<PermissionRequest>?,
|
|
||||||
token: PermissionToken?
|
|
||||||
) {
|
|
||||||
// Remember to invoke this method when the custom rationale is closed
|
|
||||||
// or just by default if you don't want to use any custom rationale.
|
|
||||||
token?.continuePermissionRequest()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.check()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,379 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.fragments
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.chibatching.kotpref.bulk
|
|
||||||
import com.karumi.dexter.Dexter
|
|
||||||
import com.karumi.dexter.MultiplePermissionsReport
|
|
||||||
import com.karumi.dexter.PermissionToken
|
|
||||||
import com.karumi.dexter.listener.PermissionRequest
|
|
||||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
|
||||||
import com.tommasoberlose.anotherwidget.models.CalendarSelector
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.FragmentCalendarSettingsBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Constants
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.global.RequestCode
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.DateHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.CustomDateActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
|
||||||
import com.tommasoberlose.anotherwidget.utils.toast
|
|
||||||
import kotlinx.android.synthetic.main.fragment_calendar_settings.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_calendar_settings.scrollView
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.Comparator
|
|
||||||
|
|
||||||
class CalendarSettingsFragment : Fragment() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun newInstance() = CalendarSettingsFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var viewModel: MainViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
|
|
||||||
val binding = DataBindingUtil.inflate<FragmentCalendarSettingsBinding>(inflater, R.layout.fragment_calendar_settings, container, false)
|
|
||||||
|
|
||||||
subscribeUi(binding, viewModel)
|
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
|
|
||||||
setupListener()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun subscribeUi(
|
|
||||||
binding: FragmentCalendarSettingsBinding,
|
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
|
||||||
viewModel.showEvents.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
binding.isCalendarEnabled = it
|
|
||||||
|
|
||||||
if (it) {
|
|
||||||
CalendarHelper.setEventUpdatesAndroidN(requireContext())
|
|
||||||
} else {
|
|
||||||
CalendarHelper.removeEventUpdatesAndroidN(requireContext())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkReadEventsPermission()
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.calendarAllDay.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
all_day_label.text =
|
|
||||||
if (it) getString(R.string.settings_all_day_subtitle_visible) else getString(R.string.settings_all_day_subtitle_gone)
|
|
||||||
}
|
|
||||||
checkReadEventsPermission()
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showDeclinedEvents.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
show_declined_events_label.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
|
||||||
}
|
|
||||||
checkReadEventsPermission()
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.secondRowInformation.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
second_row_info_label.text = getString(SettingsStringHelper.getSecondRowInfoString(it))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showDiffTime.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
show_diff_time_label.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showUntil.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
show_until_label.text = getString(SettingsStringHelper.getShowUntilString(it))
|
|
||||||
}
|
|
||||||
checkReadEventsPermission()
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showNextEvent.observe(viewLifecycleOwner, Observer {
|
|
||||||
show_multiple_events_label.setTextKeepState(if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible))
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.dateFormat.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
date_format_label.text = DateHelper.getDateText(requireContext(), Calendar.getInstance())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.calendarAppName.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
calendar_app_label.text = if (it != "") it else getString(R.string.default_calendar_app)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.eventAppName.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
event_app_label.text = if (it != "") it else getString(R.string.default_calendar_app)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
|
||||||
|
|
||||||
action_show_events.setOnClickListener {
|
|
||||||
Preferences.showEvents = !Preferences.showEvents
|
|
||||||
}
|
|
||||||
|
|
||||||
action_filter_calendar.setOnClickListener {
|
|
||||||
val calendarSelectorList: List<CalendarSelector> = CalendarHelper.getCalendarList(requireContext()).map {
|
|
||||||
CalendarSelector(
|
|
||||||
it.id,
|
|
||||||
it.displayName,
|
|
||||||
it.accountName
|
|
||||||
)
|
|
||||||
}.sortedWith(Comparator { cal1, cal2 ->
|
|
||||||
when {
|
|
||||||
cal1.accountName != cal2.accountName -> {
|
|
||||||
cal1.accountName.compareTo(cal2.accountName)
|
|
||||||
}
|
|
||||||
cal1.accountName == cal1.name -> {
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
cal2.accountName == cal2.name -> {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
cal1.name.compareTo(cal2.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (calendarSelectorList.isNotEmpty()) {
|
|
||||||
val filteredCalendarIds = CalendarHelper.getFilteredCalendarIdList()
|
|
||||||
val visibleCalendarIds = calendarSelectorList.map { it.id }.filter { id: Long -> !filteredCalendarIds.contains(id) }
|
|
||||||
|
|
||||||
val dialog = BottomSheetMenu<Long>(requireContext(), header = getString(R.string.settings_filter_calendar_subtitle), isMultiSelection = true)
|
|
||||||
.setSelectedValues(visibleCalendarIds)
|
|
||||||
|
|
||||||
calendarSelectorList.indices.forEach { index ->
|
|
||||||
if (index == 0 || calendarSelectorList[index].accountName != calendarSelectorList[index - 1].accountName) {
|
|
||||||
dialog.addItem(calendarSelectorList[index].accountName)
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.addItem(
|
|
||||||
if (calendarSelectorList[index].name == calendarSelectorList[index].accountName) getString(R.string.account_events) else calendarSelectorList[index].name,
|
|
||||||
calendarSelectorList[index].id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.addOnMultipleSelectItemListener { values ->
|
|
||||||
CalendarHelper.filterCalendar(calendarSelectorList.map { it.id }.filter { !values.contains(it) })
|
|
||||||
checkReadEventsPermission()
|
|
||||||
}.show()
|
|
||||||
} else {
|
|
||||||
activity?.toast(getString(R.string.calendar_settings_list_error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_all_day.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_all_day_title)).setSelectedValue(Preferences.calendarAllDay)
|
|
||||||
.addItem(getString(R.string.settings_all_day_subtitle_visible), true)
|
|
||||||
.addItem(getString(R.string.settings_all_day_subtitle_gone), false)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.calendarAllDay = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_declined_events.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_declined_events_title)).setSelectedValue(Preferences.showDeclinedEvents)
|
|
||||||
.addItem(getString(R.string.settings_visible), true)
|
|
||||||
.addItem(getString(R.string.settings_not_visible), false)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.showDeclinedEvents = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_multiple_events.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_multiple_events_title)).setSelectedValue(Preferences.showNextEvent)
|
|
||||||
.addItem(getString(R.string.settings_visible), true)
|
|
||||||
.addItem(getString(R.string.settings_not_visible), false)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.showNextEvent = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_diff_time.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_diff_time_title)).setSelectedValue(Preferences.showDiffTime)
|
|
||||||
.addItem(getString(R.string.settings_visible), true)
|
|
||||||
.addItem(getString(R.string.settings_not_visible), false)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.showDiffTime = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_second_row_info.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_second_row_info_title)).setSelectedValue(Preferences.secondRowInformation)
|
|
||||||
(0 .. 1).forEach {
|
|
||||||
dialog.addItem(getString(SettingsStringHelper.getSecondRowInfoString(it)), it)
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.secondRowInformation = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_until.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_until_title)).setSelectedValue(Preferences.showUntil)
|
|
||||||
intArrayOf(6,7,0,1,2,3,4,5).forEach {
|
|
||||||
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.showUntil = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_date_format.setOnClickListener {
|
|
||||||
if (Preferences.showEvents) {
|
|
||||||
val now = Calendar.getInstance()
|
|
||||||
val dialog = BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_date_format_title)).setSelectedValue(Preferences.dateFormat)
|
|
||||||
|
|
||||||
dialog.addItem(DateHelper.getDefaultDateText(requireContext(), now), "")
|
|
||||||
if (Preferences.dateFormat != "") {
|
|
||||||
dialog.addItem(DateHelper.getDateText(requireContext(), now), Preferences.dateFormat)
|
|
||||||
}
|
|
||||||
dialog.addItem(getString(R.string.custom_date_format), "-")
|
|
||||||
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
if (value == "-") {
|
|
||||||
startActivity(Intent(requireContext(), CustomDateActivity::class.java))
|
|
||||||
} else {
|
|
||||||
Preferences.dateFormat = value
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_event_app.setOnClickListener {
|
|
||||||
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java), RequestCode.EVENT_APP_REQUEST_CODE.code)
|
|
||||||
}
|
|
||||||
|
|
||||||
action_calendar_app.setOnClickListener {
|
|
||||||
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java), RequestCode.CALENDAR_APP_REQUEST_CODE.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkReadEventsPermission(showEvents: Boolean = Preferences.showEvents) {
|
|
||||||
if (activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) == true) {
|
|
||||||
show_events_label.text = if (showEvents) getString(R.string.show_events_visible) else getString(R.string.show_events_not_visible)
|
|
||||||
read_calendar_permission_alert_icon.isVisible = false
|
|
||||||
CalendarHelper.updateEventList(requireContext())
|
|
||||||
} else {
|
|
||||||
show_events_label.text = if (showEvents) getString(R.string.description_permission_calendar) else getString(R.string.show_events_not_visible)
|
|
||||||
read_calendar_permission_alert_icon.isVisible = showEvents
|
|
||||||
read_calendar_permission_alert_icon.setOnClickListener {
|
|
||||||
requirePermission()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requirePermission() {
|
|
||||||
Dexter.withContext(requireContext())
|
|
||||||
.withPermissions(
|
|
||||||
Manifest.permission.READ_CALENDAR
|
|
||||||
).withListener(object: MultiplePermissionsListener {
|
|
||||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
|
||||||
report?.let {
|
|
||||||
if (report.areAllPermissionsGranted()){
|
|
||||||
checkReadEventsPermission()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onPermissionRationaleShouldBeShown(
|
|
||||||
permissions: MutableList<PermissionRequest>?,
|
|
||||||
token: PermissionToken?
|
|
||||||
) {
|
|
||||||
// Remember to invoke this method when the custom rationale is closed
|
|
||||||
// or just by default if you don't want to use any custom rationale.
|
|
||||||
token?.continuePermissionRequest()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.check()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
when (requestCode) {
|
|
||||||
RequestCode.CALENDAR_APP_REQUEST_CODE.code -> {
|
|
||||||
Preferences.bulk {
|
|
||||||
calendarAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_calendar_app)
|
|
||||||
calendarAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RequestCode.EVENT_APP_REQUEST_CODE.code -> {
|
|
||||||
Preferences.bulk {
|
|
||||||
eventAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_event_app)
|
|
||||||
eventAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
|
||||||
val scrollPosition = scrollView.scrollY
|
|
||||||
callback.invoke()
|
|
||||||
lifecycleScope.launch {
|
|
||||||
delay(200)
|
|
||||||
scrollView.smoothScrollTo(0, scrollPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.fragments
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.chibatching.kotpref.bulk
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.FragmentClockSettingsBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Constants
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.global.RequestCode
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
|
||||||
import kotlinx.android.synthetic.main.fragment_clock_settings.*
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class ClockSettingsFragment : Fragment() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun newInstance() = ClockSettingsFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var viewModel: MainViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
|
|
||||||
val binding = DataBindingUtil.inflate<FragmentClockSettingsBinding>(inflater, R.layout.fragment_clock_settings, container, false)
|
|
||||||
|
|
||||||
subscribeUi(binding, viewModel)
|
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
|
|
||||||
setupListener()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun subscribeUi(
|
|
||||||
binding: FragmentClockSettingsBinding,
|
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
|
||||||
viewModel.showBigClockWarning.observe(viewLifecycleOwner, Observer {
|
|
||||||
large_clock_warning.isVisible = it
|
|
||||||
small_clock_warning.isVisible = !it
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showClock.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
show_clock_label.text =
|
|
||||||
if (it) getString(R.string.show_clock_visible) else getString(R.string.show_clock_not_visible)
|
|
||||||
binding.isClockVisible = it
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.clockTextSize.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
clock_text_size_label.text = String.format("%.0fsp", it)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.clockBottomMargin.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
clock_bottom_margin_label.text = when (it) {
|
|
||||||
Constants.ClockBottomMargin.NONE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_none)
|
|
||||||
Constants.ClockBottomMargin.SMALL.value -> getString(R.string.settings_clock_bottom_margin_subtitle_small)
|
|
||||||
Constants.ClockBottomMargin.LARGE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_large)
|
|
||||||
else -> getString(R.string.settings_clock_bottom_margin_subtitle_medium)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
show_next_alarm_label.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.clockAppName.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
clock_app_label.text =
|
|
||||||
if (Preferences.clockAppName != "") Preferences.clockAppName else getString(R.string.default_clock_app)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
|
||||||
action_hide_large_clock_warning.setOnClickListener {
|
|
||||||
Preferences.showBigClockWarning = false
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_clock.setOnClickListener {
|
|
||||||
Preferences.showClock = !Preferences.showClock
|
|
||||||
}
|
|
||||||
|
|
||||||
action_clock_text_size.setOnClickListener {
|
|
||||||
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.settings_clock_text_size_title)).setSelectedValue(Preferences.clockTextSize)
|
|
||||||
(46 downTo 12).filter { it % 2 == 0 }.forEach {
|
|
||||||
dialog.addItem("${it}sp", it.toFloat())
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.clockTextSize = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_clock_bottom_margin_size.setOnClickListener {
|
|
||||||
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_next_alarm_title)).setSelectedValue(Preferences.clockBottomMargin)
|
|
||||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_none), Constants.ClockBottomMargin.NONE.value)
|
|
||||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_small), Constants.ClockBottomMargin.SMALL.value)
|
|
||||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_medium), Constants.ClockBottomMargin.MEDIUM.value)
|
|
||||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_large), Constants.ClockBottomMargin.LARGE.value)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.clockBottomMargin = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_clock_app.setOnClickListener {
|
|
||||||
if (Preferences.showClock) {
|
|
||||||
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java),
|
|
||||||
RequestCode.CLOCK_APP_REQUEST_CODE.code
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_show_next_alarm.setOnClickListener {
|
|
||||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_next_alarm_title)).setSelectedValue(Preferences.showNextAlarm)
|
|
||||||
.addItem(getString(R.string.settings_visible), true)
|
|
||||||
.addItem(getString(R.string.settings_not_visible), false)
|
|
||||||
.addOnSelectItemListener { value ->
|
|
||||||
Preferences.showNextAlarm = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
if (resultCode == Activity.RESULT_OK && requestCode == RequestCode.CLOCK_APP_REQUEST_CODE.code) {
|
|
||||||
Preferences.bulk {
|
|
||||||
clockAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_clock_app)
|
|
||||||
clockAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
|
||||||
val scrollPosition = scrollView.scrollY
|
|
||||||
callback.invoke()
|
|
||||||
lifecycleScope.launch {
|
|
||||||
delay(200)
|
|
||||||
scrollView.smoothScrollTo(0, scrollPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,213 +0,0 @@
|
|||||||
package com.tommasoberlose.anotherwidget.ui.fragments
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.tommasoberlose.anotherwidget.R
|
|
||||||
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
|
|
||||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
|
||||||
import com.tommasoberlose.anotherwidget.databinding.FragmentGeneralSettingsBinding
|
|
||||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
|
||||||
import com.tommasoberlose.anotherwidget.global.RequestCode
|
|
||||||
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
|
||||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
|
||||||
import kotlinx.android.synthetic.main.fragment_general_settings.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
|
|
||||||
class GeneralSettingsFragment : Fragment() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun newInstance() = GeneralSettingsFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var viewModel: MainViewModel
|
|
||||||
private lateinit var colors: IntArray
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
|
|
||||||
val binding = DataBindingUtil.inflate<FragmentGeneralSettingsBinding>(inflater, R.layout.fragment_general_settings, container, false)
|
|
||||||
|
|
||||||
subscribeUi(viewModel)
|
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
|
|
||||||
setupListener()
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
colors = lazyColors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun subscribeUi(
|
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
|
||||||
|
|
||||||
viewModel.textMainSize.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
main_text_size_label.text = String.format("%.0fsp", it)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.textSecondSize.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
second_text_size_label.text = String.format("%.0fsp", it)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.textGlobalColor.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
try {
|
|
||||||
Color.parseColor(it)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Preferences.textGlobalColor = "#FFFFFF"
|
|
||||||
}
|
|
||||||
font_color_label.text = it.toUpperCase()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.textShadow.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
text_shadow_label.text = getString(SettingsStringHelper.getTextShadowString(it))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.customFont.observe(viewLifecycleOwner, Observer {
|
|
||||||
maintainScrollPosition {
|
|
||||||
custom_font_label.text = getString(SettingsStringHelper.getCustomFontLabel(it))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
|
||||||
val scrollPosition = scrollView.scrollY
|
|
||||||
callback.invoke()
|
|
||||||
lifecycleScope.launch {
|
|
||||||
delay(200)
|
|
||||||
scrollView.smoothScrollTo(0, scrollPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupListener() {
|
|
||||||
action_main_text_size.setOnClickListener {
|
|
||||||
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(Preferences.textMainSize)
|
|
||||||
(32 downTo 10).filter { it % 2 == 0 }.forEach {
|
|
||||||
dialog.addItem("${it}sp", it.toFloat())
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.textMainSize = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_second_text_size.setOnClickListener {
|
|
||||||
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(Preferences.textSecondSize)
|
|
||||||
(28 downTo 10).filter { it % 2 == 0 }.forEach {
|
|
||||||
dialog.addItem("${it}sp", it.toFloat())
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.textSecondSize = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_font_color.setOnClickListener {
|
|
||||||
val textColor = try {
|
|
||||||
Color.parseColor(Preferences.textGlobalColor)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Preferences.textGlobalColor = "#FFFFFF"
|
|
||||||
Color.parseColor(Preferences.textGlobalColor)
|
|
||||||
}
|
|
||||||
BottomSheetColorPicker(requireContext(),
|
|
||||||
colors = colors,
|
|
||||||
header = getString(R.string.settings_font_color_title),
|
|
||||||
selected = textColor,
|
|
||||||
onColorSelected = { color: Int ->
|
|
||||||
val colorString = Integer.toHexString(color)
|
|
||||||
Preferences.textGlobalColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
|
||||||
}
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_text_shadow.setOnClickListener {
|
|
||||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.title_text_shadow)).setSelectedValue(Preferences.textShadow)
|
|
||||||
(2 downTo 0).forEach {
|
|
||||||
dialog.addItem(getString(SettingsStringHelper.getTextShadowString(it)), it)
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.textShadow = value
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
action_custom_font.setOnClickListener {
|
|
||||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_custom_font_title)).setSelectedValue(Preferences.customFont)
|
|
||||||
(0..1).forEach {
|
|
||||||
dialog.addItem(getString(SettingsStringHelper.getCustomFontLabel(it)), it)
|
|
||||||
}
|
|
||||||
dialog.addOnSelectItemListener { value ->
|
|
||||||
Preferences.customFont = value
|
|
||||||
}.show()
|
|
||||||
|
|
||||||
/*
|
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
|
||||||
intent.type = "* / *" TO FIX WITHOUT SPACE
|
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
|
|
||||||
try {
|
|
||||||
startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"), Constants.CUSTOM_FONT_CHOOSER_REQUEST_CODE)
|
|
||||||
} catch (ex: android.content.ActivityNotFoundException) {
|
|
||||||
Toast.makeText(this, "Please install a File Manager.", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
when (requestCode) {
|
|
||||||
RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code -> {
|
|
||||||
/*val uri = data.data
|
|
||||||
Log.d("AW", "File Uri: " + uri.toString())
|
|
||||||
val path = Util.getPath(this, uri)
|
|
||||||
Log.d("AW", "File Path: " + path)
|
|
||||||
SP.edit()
|
|
||||||
.putString(Constants.PREF_CUSTOM_FONT_FILE, path)
|
|
||||||
.commit()
|
|
||||||
sendBroadcast(Intent(Constants.ACTION_TIME_UPDATE))
|
|
||||||
updateSettings()*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user