mirror of https://github.com/tasks/tasks
Compare commits
383 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
fb7960bf26 | 16 hours ago |
![]() |
fecfea8df5 | 16 hours ago |
![]() |
6ce170533e | 17 hours ago |
![]() |
ad4d938b94 | 17 hours ago |
![]() |
1702249abc | 17 hours ago |
![]() |
b52d954731 | 17 hours ago |
![]() |
a1a02faf3d | 17 hours ago |
![]() |
9307b8fb7d | 17 hours ago |
![]() |
8b345316a8 | 24 hours ago |
![]() |
ab0f123c8a | 24 hours ago |
![]() |
36a28b6210 | 24 hours ago |
![]() |
d2ae1008f5 | 24 hours ago |
![]() |
17af8ab373 | 1 day ago |
![]() |
e78d452d7b | 1 day ago |
![]() |
e9ddf29b9c | 1 day ago |
![]() |
898c88bec9 | 1 day ago |
![]() |
184e7def49 | 1 day ago |
![]() |
d8186e5fe4 | 1 day ago |
![]() |
a4cd0829b0 | 2 days ago |
![]() |
9a693177db | 2 days ago |
![]() |
58955bd0a1 | 2 days ago |
![]() |
95ecac8e7c | 2 days ago |
![]() |
57395423c6 | 2 days ago |
![]() |
b1613e9845 | 2 days ago |
![]() |
dcd70c7bc2 | 2 days ago |
![]() |
c1e2eb7cd0 | 3 days ago |
![]() |
145ea03714 | 3 days ago |
![]() |
bada09f5c2 | 3 days ago |
![]() |
007c536312 | 3 days ago |
![]() |
28de989a05 | 3 days ago |
![]() |
97c3852f2f | 4 days ago |
![]() |
f739cac8b4 | 4 days ago |
![]() |
cd6a474cce | 4 days ago |
![]() |
4ffc11903e | 4 days ago |
![]() |
70d6cc63ca | 4 days ago |
![]() |
6e97e602c9 | 4 days ago |
![]() |
3251becf9b | 5 days ago |
![]() |
d9293c7262 | 5 days ago |
![]() |
929a01cd8c | 5 days ago |
![]() |
0d5803b9ca | 5 days ago |
![]() |
bbaaf27386 | 6 days ago |
![]() |
4c1121869d | 7 days ago |
![]() |
b918e87e05 | 1 week ago |
![]() |
df8f637239 | 1 week ago |
![]() |
9fad43c6c9 | 1 week ago |
![]() |
eb95cd24d7 | 1 week ago |
![]() |
30cb374a21 | 2 weeks ago |
![]() |
b09a8967e4 | 2 weeks ago |
![]() |
39b56296bd | 2 weeks ago |
![]() |
1d8d2efce6 | 3 weeks ago |
![]() |
c9af39b6ba | 3 weeks ago |
![]() |
f8bb045d76 | 3 weeks ago |
![]() |
4040a2379b | 3 weeks ago |
![]() |
c4ee7479ca | 3 weeks ago |
![]() |
be861597ef | 3 weeks ago |
![]() |
f27332595d | 3 weeks ago |
![]() |
ea7f051d85 | 3 weeks ago |
![]() |
8be7fab033 | 3 weeks ago |
![]() |
d6e0c0bdcf | 3 weeks ago |
![]() |
5ec02011f8 | 3 weeks ago |
![]() |
b35090cd43 | 3 weeks ago |
![]() |
92f62450ae | 3 weeks ago |
![]() |
3ca6912492 | 3 weeks ago |
![]() |
080b1428dd | 3 weeks ago |
![]() |
f67c3bc56c | 3 weeks ago |
![]() |
5d0e88a620 | 3 weeks ago |
![]() |
c5d5795fe2 | 3 weeks ago |
![]() |
3bbc0e0ab0 | 3 weeks ago |
![]() |
009a195580 | 3 weeks ago |
![]() |
772f69d8c0 | 3 weeks ago |
![]() |
4229bf7067 | 3 weeks ago |
![]() |
212a4b0a3d | 3 weeks ago |
![]() |
4ddfe937b0 | 3 weeks ago |
![]() |
19de0e08a5 | 3 weeks ago |
![]() |
60211355e0 | 3 weeks ago |
![]() |
17d218aa4e | 3 weeks ago |
![]() |
505c8c29d5 | 3 weeks ago |
![]() |
7149308c97 | 3 weeks ago |
![]() |
2c5a497007 | 3 weeks ago |
![]() |
09f53fe1e5 | 3 weeks ago |
![]() |
5da4183aed | 3 weeks ago |
![]() |
d35912e503 | 3 weeks ago |
![]() |
82fd99f83e | 3 weeks ago |
![]() |
f944becea1 | 3 weeks ago |
![]() |
acd713dc5b | 3 weeks ago |
![]() |
1a93c87ad9 | 4 weeks ago |
![]() |
c4e25b8b15 | 4 weeks ago |
![]() |
e11c0d2528 | 4 weeks ago |
![]() |
2fc6833854 | 4 weeks ago |
![]() |
4a2fb13d10 | 4 weeks ago |
![]() |
a2572e2dee | 4 weeks ago |
![]() |
64e05c9f8f | 4 weeks ago |
![]() |
ad833b5f49 | 4 weeks ago |
![]() |
eea944cc7b | 4 weeks ago |
![]() |
c82dfc7d39 | 4 weeks ago |
![]() |
8607f9556a | 4 weeks ago |
![]() |
f338e84d46 | 1 month ago |
![]() |
9ee739627e | 1 month ago |
![]() |
a49c233584 | 1 month ago |
![]() |
74fca07c1b | 1 month ago |
![]() |
5bd0cef42e | 1 month ago |
![]() |
4c245edbb4 | 1 month ago |
![]() |
97a3f074d0 | 1 month ago |
![]() |
86ecd3cf81 | 1 month ago |
![]() |
07a2eda5ea | 1 month ago |
![]() |
09ffbdd036 | 1 month ago |
![]() |
60f22146ca | 1 month ago |
![]() |
c11225abaf | 1 month ago |
![]() |
133ea493e3 | 1 month ago |
![]() |
0ba901be69 | 1 month ago |
![]() |
ebe5e5c009 | 1 month ago |
![]() |
d556863fda | 1 month ago |
![]() |
55adbc2025 | 1 month ago |
![]() |
06c4255886 | 1 month ago |
![]() |
4734a99bae | 1 month ago |
![]() |
a6a8cac8e4 | 1 month ago |
![]() |
c3fc9a57cc | 1 month ago |
![]() |
6e14d07d0c | 1 month ago |
![]() |
6118121698 | 1 month ago |
![]() |
6bf3bd4d08 | 1 month ago |
![]() |
065be79355 | 1 month ago |
![]() |
f8f8ba3c51 | 1 month ago |
![]() |
89465f36b3 | 1 month ago |
![]() |
1380a34ffa | 1 month ago |
![]() |
10af5280a3 | 1 month ago |
![]() |
8c0f7b952d | 1 month ago |
![]() |
65362b203f | 1 month ago |
![]() |
3327f97a17 | 1 month ago |
![]() |
c9fc02a42e | 1 month ago |
![]() |
93670bb9e4 | 1 month ago |
![]() |
1fc6a50d0b | 1 month ago |
![]() |
e1ef924909 | 1 month ago |
![]() |
686cb5d346 | 1 month ago |
![]() |
ebec25c4cb | 1 month ago |
![]() |
c140f7e673 | 1 month ago |
![]() |
efbcf11a4a | 1 month ago |
![]() |
6adee85a37 | 1 month ago |
![]() |
5c8643110b | 1 month ago |
![]() |
abd13aeb75 | 1 month ago |
![]() |
c210fe1893 | 1 month ago |
![]() |
26aa916c20 | 1 month ago |
![]() |
1eff2d1cd5 | 1 month ago |
![]() |
c90e683ea3 | 1 month ago |
![]() |
3cd0295b71 | 1 month ago |
![]() |
95c351e9fd | 1 month ago |
![]() |
4ddb7816f1 | 1 month ago |
![]() |
91c30f7bbf | 2 months ago |
![]() |
3f4398b6e0 | 2 months ago |
![]() |
c822e989a3 | 2 months ago |
![]() |
da146723e5 | 2 months ago |
![]() |
931626c84a | 2 months ago |
![]() |
c534632c52 | 2 months ago |
![]() |
c1347a7455 | 2 months ago |
![]() |
9544909a58 | 2 months ago |
![]() |
5c10dce2b9 | 2 months ago |
![]() |
584d4a5cbb | 2 months ago |
![]() |
7c68a7fa59 | 2 months ago |
![]() |
215cc838ef | 2 months ago |
![]() |
d60472d1bc | 2 months ago |
![]() |
f84a37a60a | 2 months ago |
![]() |
7fb85b6da1 | 2 months ago |
![]() |
dc90e583e4 | 2 months ago |
![]() |
0eac5f61eb | 2 months ago |
![]() |
c686ce883d | 2 months ago |
![]() |
ab25398cd0 | 2 months ago |
![]() |
3b1c133d22 | 2 months ago |
![]() |
3bfd0ab4f8 | 2 months ago |
![]() |
ffc0113d7f | 2 months ago |
![]() |
9de9718ad5 | 2 months ago |
![]() |
a7d2c9c406 | 2 months ago |
![]() |
b3006b9ac2 | 2 months ago |
![]() |
de3ef1f9c9 | 2 months ago |
![]() |
ce9e722a3f | 2 months ago |
![]() |
4b892a0eb1 | 2 months ago |
![]() |
e6e275834a | 2 months ago |
![]() |
782f4d6d7c | 2 months ago |
![]() |
a1da71d3e1 | 2 months ago |
![]() |
c793a300cc | 2 months ago |
![]() |
bf84bf9e82 | 2 months ago |
![]() |
363b29babb | 2 months ago |
![]() |
c1ff953f5c | 2 months ago |
![]() |
63482e5db9 | 2 months ago |
![]() |
2f7dc0c7f1 | 2 months ago |
![]() |
d672507fae | 2 months ago |
![]() |
ce2a3c8a3f | 2 months ago |
![]() |
9cd114d68b | 3 months ago |
![]() |
0e663f0e08 | 3 months ago |
![]() |
1d1efd008d | 3 months ago |
![]() |
26ab3d5866 | 3 months ago |
![]() |
9a4fcbbd39 | 3 months ago |
![]() |
72bfda9224 | 3 months ago |
![]() |
1067de4183 | 3 months ago |
![]() |
d686b8c7e0 | 3 months ago |
![]() |
b2efb42d55 | 3 months ago |
![]() |
3448808c94 | 3 months ago |
![]() |
06a9626052 | 3 months ago |
![]() |
e92ab7f7e1 | 3 months ago |
![]() |
4ff7b18c0f | 3 months ago |
![]() |
91887f6b17 | 3 months ago |
![]() |
cf30b56098 | 3 months ago |
![]() |
9bcadaab5a | 3 months ago |
![]() |
be766074b0 | 3 months ago |
![]() |
64a42a3f61 | 3 months ago |
![]() |
7b65ba6f06 | 3 months ago |
![]() |
ac2b270e9e | 3 months ago |
![]() |
db2ea0a039 | 3 months ago |
![]() |
08b78fe9f4 | 3 months ago |
![]() |
1a1301ae3e | 3 months ago |
![]() |
d00061aa7f | 3 months ago |
![]() |
45add6ab32 | 3 months ago |
![]() |
af43737c4e | 3 months ago |
![]() |
dd40e59b17 | 3 months ago |
![]() |
13f3248a01 | 3 months ago |
![]() |
f6972e3e30 | 3 months ago |
![]() |
83cf48a836 | 3 months ago |
![]() |
b7b4747a04 | 3 months ago |
![]() |
6bec2ceef0 | 3 months ago |
![]() |
d1e60d6512 | 3 months ago |
![]() |
2b85089d3a | 3 months ago |
![]() |
2a0ef9feb6 | 3 months ago |
![]() |
33adbbd884 | 3 months ago |
![]() |
c25eb2e0c5 | 3 months ago |
![]() |
14026356eb | 3 months ago |
![]() |
b328651dd4 | 3 months ago |
![]() |
a0e9bfabeb | 3 months ago |
![]() |
a1ad421b33 | 3 months ago |
![]() |
3488a08af1 | 3 months ago |
![]() |
b71d1af516 | 3 months ago |
![]() |
041dce8617 | 3 months ago |
![]() |
3d92ca78dd | 3 months ago |
![]() |
a32fce2d8b | 3 months ago |
![]() |
4fb3cda173 | 3 months ago |
![]() |
f33cc896dd | 3 months ago |
![]() |
4d1d6a06a8 | 3 months ago |
![]() |
2202516688 | 3 months ago |
![]() |
d4a5008ecb | 3 months ago |
![]() |
08189e10f1 | 3 months ago |
![]() |
d3e4c066d8 | 3 months ago |
![]() |
bbc5ae4d6d | 3 months ago |
![]() |
c6cc00cf07 | 3 months ago |
![]() |
22e8720021 | 3 months ago |
![]() |
a3ce98f0ea | 3 months ago |
![]() |
258f607d52 | 3 months ago |
![]() |
927acae7e4 | 3 months ago |
![]() |
49ad9bafe3 | 3 months ago |
![]() |
6df616d9ce | 3 months ago |
![]() |
157668e35a | 3 months ago |
![]() |
efdf343869 | 3 months ago |
![]() |
5606df17c5 | 3 months ago |
![]() |
fc3b4971f4 | 3 months ago |
![]() |
6a1699bb33 | 3 months ago |
![]() |
e49303d5ca | 3 months ago |
![]() |
4b55569b51 | 4 months ago |
![]() |
2d7145cde3 | 4 months ago |
![]() |
f2ab8bed95 | 4 months ago |
![]() |
a5bc4cf536 | 4 months ago |
![]() |
1b35372b3a | 4 months ago |
![]() |
c0fd4bf66a | 4 months ago |
![]() |
5d366f0d61 | 4 months ago |
![]() |
d0635ac6f3 | 4 months ago |
![]() |
8d4cf4daa5 | 4 months ago |
![]() |
d1e439e70e | 4 months ago |
![]() |
4d4c3e5193 | 4 months ago |
![]() |
20f87061fd | 4 months ago |
![]() |
c03e3747c6 | 4 months ago |
![]() |
925b1b9124 | 4 months ago |
![]() |
43db712f64 | 4 months ago |
![]() |
9d33a73ee6 | 4 months ago |
![]() |
391c600ce2 | 4 months ago |
![]() |
ee4ae94817 | 4 months ago |
![]() |
70b4be1447 | 4 months ago |
![]() |
bc54d92789 | 4 months ago |
![]() |
2f34724b95 | 4 months ago |
![]() |
940fdc28dd | 4 months ago |
![]() |
68542fce38 | 4 months ago |
![]() |
7ba2977100 | 5 months ago |
![]() |
cb242539f0 | 5 months ago |
![]() |
304841f2c3 | 5 months ago |
![]() |
819ea797e6 | 5 months ago |
![]() |
2dbea57262 | 5 months ago |
![]() |
516a916fd5 | 5 months ago |
![]() |
3bd52efc80 | 5 months ago |
![]() |
64af955ea7 | 5 months ago |
![]() |
4cc5ec9639 | 5 months ago |
![]() |
0d9292e53a | 5 months ago |
![]() |
732ccf1913 | 5 months ago |
![]() |
a2852bdbbf | 5 months ago |
![]() |
68790ad401 | 5 months ago |
![]() |
e9afacb595 | 5 months ago |
![]() |
cf182aceab | 5 months ago |
![]() |
db889d233a | 5 months ago |
![]() |
457b89c092 | 5 months ago |
![]() |
ad53af1b6a | 5 months ago |
![]() |
2c32b08c97 | 5 months ago |
![]() |
a2fcf57c9e | 5 months ago |
![]() |
59a61325f2 | 5 months ago |
![]() |
38a6064677 | 5 months ago |
![]() |
67daccf3e8 | 5 months ago |
![]() |
dfe829d2a1 | 5 months ago |
![]() |
23c64f4d28 | 5 months ago |
![]() |
e4b8f694f3 | 5 months ago |
![]() |
e667c80731 | 5 months ago |
![]() |
909b077e25 | 5 months ago |
![]() |
e6fab9ad45 | 5 months ago |
![]() |
9474f5b7af | 5 months ago |
![]() |
1ee051d768 | 5 months ago |
![]() |
f42edaa158 | 5 months ago |
![]() |
b97eade59c | 5 months ago |
![]() |
41aa1ca65f | 5 months ago |
![]() |
3e9a13ea14 | 5 months ago |
![]() |
d966e8a12b | 5 months ago |
![]() |
8ba4e64994 | 5 months ago |
![]() |
ee792f1ceb | 5 months ago |
![]() |
caa09163a1 | 5 months ago |
![]() |
d270abf5b3 | 5 months ago |
![]() |
1ef530abad | 5 months ago |
![]() |
df26a6dbb9 | 5 months ago |
![]() |
1882c3b7e0 | 6 months ago |
![]() |
cb53a0ca9f | 6 months ago |
![]() |
b2fdef1ae7 | 6 months ago |
![]() |
defb16ce95 | 6 months ago |
![]() |
823f99b28a | 6 months ago |
![]() |
6df872b1a1 | 6 months ago |
![]() |
133b960583 | 6 months ago |
![]() |
2e6753faec | 6 months ago |
![]() |
cb07c2c267 | 6 months ago |
![]() |
23757ab320 | 6 months ago |
![]() |
1b6ce0e48e | 6 months ago |
![]() |
5af012068f | 6 months ago |
![]() |
6c9ffa57d7 | 7 months ago |
![]() |
52c54b1eac | 7 months ago |
![]() |
c8d81b44b6 | 7 months ago |
![]() |
ef27a50e42 | 7 months ago |
![]() |
bde1356e7f | 7 months ago |
![]() |
6c031925ba | 7 months ago |
![]() |
8058414137 | 7 months ago |
![]() |
3e37ea50f0 | 7 months ago |
![]() |
62f5a9c492 | 7 months ago |
![]() |
a84fd65722 | 7 months ago |
![]() |
517b2d8f1b | 7 months ago |
![]() |
90942bf0be | 7 months ago |
![]() |
83c3d1c4ba | 7 months ago |
![]() |
6362ece569 | 7 months ago |
![]() |
8df85041b8 | 7 months ago |
![]() |
6d85af4c34 | 7 months ago |
![]() |
63f001dd72 | 7 months ago |
![]() |
de49a50944 | 7 months ago |
![]() |
df20d2f593 | 7 months ago |
![]() |
fd16772236 | 7 months ago |
![]() |
b77caac255 | 7 months ago |
![]() |
ad058ed09b | 7 months ago |
![]() |
8312113d7b | 7 months ago |
![]() |
582ebad0f0 | 7 months ago |
![]() |
684c47184a | 7 months ago |
![]() |
ac7a519e4e | 7 months ago |
![]() |
5c2b41af9d | 7 months ago |
![]() |
13986cf380 | 7 months ago |
![]() |
c4f0b404e9 | 7 months ago |
![]() |
145b5afbc6 | 7 months ago |
![]() |
0b87a206fe | 7 months ago |
![]() |
d0e70ceea8 | 7 months ago |
![]() |
bf3546a878 | 7 months ago |
![]() |
8895acbf6b | 7 months ago |
![]() |
a52b1200f5 | 7 months ago |
![]() |
23964e807a | 8 months ago |
![]() |
287b106dd4 | 8 months ago |
![]() |
33bab626e0 | 8 months ago |
![]() |
a980cd75cc | 8 months ago |
![]() |
7eac4ac223 | 8 months ago |
![]() |
82cb2f7d3f | 8 months ago |
![]() |
da2646597c | 8 months ago |
![]() |
495855133c | 8 months ago |
![]() |
242cb61662 | 8 months ago |
![]() |
ab8886f3dc | 8 months ago |
![]() |
e48e92d2e6 | 8 months ago |
![]() |
5f22f5cd38 | 8 months ago |
![]() |
8a47cc2934 | 8 months ago |
![]() |
0d94729d37 | 8 months ago |
![]() |
c8bfb67b50 | 8 months ago |
![]() |
0a36e58525 | 8 months ago |
![]() |
94a719cb66 | 8 months ago |
![]() |
b5748aa8e6 | 8 months ago |
![]() |
7fd5647cb8 | 8 months ago |
@ -1 +1 @@
|
|||||||
3.2.2
|
3.3.3
|
||||||
|
@ -1,106 +1,290 @@
|
|||||||
package com.todoroo.astrid.alarms
|
package com.todoroo.astrid.alarms
|
||||||
|
|
||||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
import dagger.hilt.android.testing.UninstallModules
|
import dagger.hilt.android.testing.UninstallModules
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.tasks.data.Alarm
|
import org.tasks.SuspendFreeze.Companion.freezeAt
|
||||||
import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME
|
import org.tasks.data.createDueDate
|
||||||
import org.tasks.data.Alarm.Companion.TYPE_RANDOM
|
import org.tasks.data.dao.TaskDao
|
||||||
import org.tasks.data.Alarm.Companion.TYPE_SNOOZE
|
import org.tasks.data.entity.Alarm
|
||||||
import org.tasks.data.Alarm.Companion.whenDue
|
import org.tasks.data.entity.Notification
|
||||||
import org.tasks.data.Alarm.Companion.whenOverdue
|
import org.tasks.data.entity.Task
|
||||||
import org.tasks.data.AlarmDao
|
|
||||||
import org.tasks.data.TaskDao
|
|
||||||
import org.tasks.date.DateTimeUtils.newDateTime
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
import org.tasks.injection.InjectingTestCase
|
||||||
import org.tasks.injection.ProductionModule
|
import org.tasks.injection.ProductionModule
|
||||||
import org.tasks.jobs.AlarmEntry
|
|
||||||
import org.tasks.jobs.NotificationQueue
|
|
||||||
import org.tasks.makers.TaskMaker.COMPLETION_TIME
|
|
||||||
import org.tasks.makers.TaskMaker.DELETION_TIME
|
|
||||||
import org.tasks.makers.TaskMaker.DUE_DATE
|
|
||||||
import org.tasks.makers.TaskMaker.DUE_TIME
|
|
||||||
import org.tasks.makers.TaskMaker.REMINDER_LAST
|
|
||||||
import org.tasks.makers.TaskMaker.newTask
|
|
||||||
import org.tasks.time.DateTime
|
import org.tasks.time.DateTime
|
||||||
|
import org.tasks.time.DateTimeUtils2
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@UninstallModules(ProductionModule::class)
|
@UninstallModules(ProductionModule::class)
|
||||||
@HiltAndroidTest
|
@HiltAndroidTest
|
||||||
class AlarmJobServiceTest : InjectingTestCase() {
|
class AlarmJobServiceTest : InjectingTestCase() {
|
||||||
@Inject lateinit var alarmDao: AlarmDao
|
|
||||||
@Inject lateinit var taskDao: TaskDao
|
@Inject lateinit var taskDao: TaskDao
|
||||||
@Inject lateinit var jobs: NotificationQueue
|
|
||||||
@Inject lateinit var alarmService: AlarmService
|
@Inject lateinit var alarmService: AlarmService
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun scheduleAlarm() = runBlocking {
|
fun testNoAlarms() = runBlocking {
|
||||||
val task = taskDao.createNew(newTask())
|
testResults(emptyList(), 0)
|
||||||
val alarm = insertAlarm(Alarm(task, DateTime(2017, 9, 24, 19, 57).millis, TYPE_DATE_TIME))
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun futureAlarmWithNoPastAlarm() = runBlocking {
|
||||||
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
|
taskDao.insert(
|
||||||
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 18).millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
|
||||||
|
|
||||||
verify(AlarmEntry(alarm, task, DateTime(2017, 9, 24, 19, 57).millis, TYPE_DATE_TIME))
|
testResults(emptyList(), DateTime(2024, 5, 18, 18, 0).millis)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun ignoreStaleAlarm() = runBlocking {
|
fun pastAlarmWithNoFutureAlarm() = runBlocking {
|
||||||
val alarmTime = DateTime(2017, 9, 24, 19, 57)
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
val task = taskDao.createNew(newTask(with(REMINDER_LAST, alarmTime.endOfMinute())))
|
taskDao.insert(
|
||||||
alarmDao.insert(Alarm(task, alarmTime.millis, TYPE_DATE_TIME))
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
|
||||||
|
|
||||||
verify()
|
testResults(
|
||||||
|
listOf(
|
||||||
|
Notification(
|
||||||
|
taskId = 1L,
|
||||||
|
timestamp = DateTimeUtils2.currentTimeMillis(),
|
||||||
|
type = Alarm.TYPE_REL_END
|
||||||
|
)
|
||||||
|
),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun dontScheduleReminderForCompletedTask() = runBlocking {
|
fun pastRecurringAlarmWithFutureRecurrence() = runBlocking {
|
||||||
val task = taskDao.insert(
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
newTask(
|
taskDao.insert(
|
||||||
with(DUE_DATE, newDateTime()),
|
Task(
|
||||||
with(COMPLETION_TIME, newDateTime())
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
1,
|
||||||
|
mutableSetOf(
|
||||||
|
Alarm(
|
||||||
|
type = Alarm.TYPE_REL_END,
|
||||||
|
repeat = 1,
|
||||||
|
interval = TimeUnit.HOURS.toMillis(6)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
alarmDao.insert(whenDue(task))
|
|
||||||
|
|
||||||
verify()
|
testResults(
|
||||||
|
listOf(
|
||||||
|
Notification(
|
||||||
|
taskId = 1L,
|
||||||
|
timestamp = DateTimeUtils2.currentTimeMillis(),
|
||||||
|
type = Alarm.TYPE_REL_END
|
||||||
|
)
|
||||||
|
),
|
||||||
|
DateTime(2024, 5, 18, 0, 0).millis
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun dontScheduleReminderForDeletedTask() = runBlocking {
|
fun pastAlarmsRemoveSnoozed() = runBlocking {
|
||||||
val task = taskDao.insert(
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
newTask(
|
taskDao.insert(
|
||||||
with(DUE_DATE, newDateTime()),
|
Task(
|
||||||
with(DELETION_TIME, newDateTime())
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
1,
|
||||||
|
mutableSetOf(
|
||||||
|
Alarm(type = Alarm.TYPE_REL_END),
|
||||||
|
Alarm(time = DateTimeUtils2.currentTimeMillis(), type = Alarm.TYPE_SNOOZE)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
alarmDao.insert(whenDue(task))
|
|
||||||
|
|
||||||
verify()
|
testResults(
|
||||||
|
listOf(
|
||||||
|
Notification(
|
||||||
|
taskId = 1L,
|
||||||
|
timestamp = DateTimeUtils2.currentTimeMillis(),
|
||||||
|
type = Alarm.TYPE_REL_END
|
||||||
|
)
|
||||||
|
),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf(Alarm(id = 1, task = 1, time = 0, type = Alarm.TYPE_REL_END)),
|
||||||
|
alarmService.getAlarms(1)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun snoozeOverridesAll() = runBlocking {
|
fun alarmsOneMinuteApart() = runBlocking {
|
||||||
val now = newDateTime()
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
val task = taskDao.insert(newTask(with(DUE_TIME, now)))
|
taskDao.insert(
|
||||||
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY_TIME,
|
||||||
|
DateTime(2024, 5, 17, 23, 20).millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
|
||||||
|
taskDao.insert(Task())
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
taskId = 2,
|
||||||
|
alarms = mutableSetOf(
|
||||||
|
Alarm(
|
||||||
|
type = Alarm.TYPE_SNOOZE,
|
||||||
|
time = DateTime(2024, 5, 17, 23, 21).millis)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testResults(
|
||||||
|
listOf(
|
||||||
|
Notification(
|
||||||
|
taskId = 1L,
|
||||||
|
timestamp = DateTimeUtils2.currentTimeMillis(),
|
||||||
|
type = Alarm.TYPE_REL_END
|
||||||
|
)
|
||||||
|
),
|
||||||
|
DateTime(2024, 5, 17, 23, 21).millis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
alarmDao.insert(whenDue(task))
|
@Test
|
||||||
alarmDao.insert(whenOverdue(task))
|
fun futureSnoozeOverrideOverdue() = runBlocking {
|
||||||
alarmDao.insert(Alarm(task, DateUtilities.ONE_HOUR, TYPE_RANDOM))
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
val alarm = alarmDao.insert(Alarm(task, now.plusMonths(12).millis, TYPE_SNOOZE))
|
taskDao.insert(
|
||||||
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
1,
|
||||||
|
mutableSetOf(
|
||||||
|
Alarm(type = Alarm.TYPE_REL_END),
|
||||||
|
Alarm(
|
||||||
|
time = DateTimeUtils2.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5),
|
||||||
|
type = Alarm.TYPE_SNOOZE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
verify(AlarmEntry(alarm, task, now.plusMonths(12).millis, TYPE_SNOOZE))
|
testResults(
|
||||||
|
emptyList(),
|
||||||
|
DateTimeUtils2.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun ignoreStaleAlarm() = runBlocking {
|
||||||
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
|
taskDao.insert(
|
||||||
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
),
|
||||||
|
reminderLast = DateTime(2024, 5, 17, 18, 0).millis,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
1,
|
||||||
|
mutableSetOf(Alarm(type = Alarm.TYPE_REL_END))
|
||||||
|
)
|
||||||
|
|
||||||
|
testResults(
|
||||||
|
emptyList(),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun insertAlarm(alarm: Alarm): Long {
|
@Test
|
||||||
alarm.id = alarmDao.insert(alarm)
|
fun dontScheduleForCompletedTask() = runBlocking {
|
||||||
return alarm.id
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
|
taskDao.insert(
|
||||||
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
),
|
||||||
|
completionDate = DateTime(2024, 5, 17, 14, 0).millis,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
1,
|
||||||
|
mutableSetOf(Alarm(type = Alarm.TYPE_REL_END))
|
||||||
|
)
|
||||||
|
|
||||||
|
testResults(
|
||||||
|
emptyList(),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun verify(vararg alarms: AlarmEntry) {
|
@Test
|
||||||
alarmService.scheduleAllAlarms()
|
fun dontScheduleForDeletedTask() = runBlocking {
|
||||||
|
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
||||||
|
taskDao.insert(
|
||||||
|
Task(
|
||||||
|
dueDate = createDueDate(
|
||||||
|
Task.URGENCY_SPECIFIC_DAY,
|
||||||
|
DateTime(2024, 5, 17).millis
|
||||||
|
),
|
||||||
|
deletionDate = DateTime(2024, 5, 17, 14, 0).millis,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
alarmService.synchronizeAlarms(
|
||||||
|
1,
|
||||||
|
mutableSetOf(Alarm(type = Alarm.TYPE_REL_END))
|
||||||
|
)
|
||||||
|
|
||||||
|
testResults(
|
||||||
|
emptyList(),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(alarms.toList(), jobs.getJobs())
|
private suspend fun testResults(notifications: List<Notification>, nextAlarm: Long) {
|
||||||
|
val actualNextAlarm = alarmService.triggerAlarms {
|
||||||
|
assertEquals(notifications, it)
|
||||||
|
it.forEach { taskDao.setLastNotified(it.taskId, DateTimeUtils2.currentTimeMillis()) }
|
||||||
|
}
|
||||||
|
assertEquals(nextAlarm, actualNextAlarm)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,50 +1,68 @@
|
|||||||
package com.todoroo.astrid.repeats
|
package com.todoroo.astrid.repeats
|
||||||
|
|
||||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
import org.tasks.data.entity.Task
|
||||||
|
import com.todoroo.astrid.service.TaskCompleter
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
import dagger.hilt.android.testing.UninstallModules
|
import dagger.hilt.android.testing.UninstallModules
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertFalse
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.tasks.data.TaskDao
|
import org.tasks.data.dao.TaskDao
|
||||||
import org.tasks.injection.InjectingTestCase
|
import org.tasks.injection.InjectingTestCase
|
||||||
import org.tasks.injection.ProductionModule
|
import org.tasks.injection.ProductionModule
|
||||||
import org.tasks.makers.TaskMaker.COMPLETION_TIME
|
import org.tasks.time.DateTimeUtils2.currentTimeMillis
|
||||||
import org.tasks.makers.TaskMaker.PARENT
|
|
||||||
import org.tasks.makers.TaskMaker.RECUR
|
|
||||||
import org.tasks.makers.TaskMaker.newTask
|
|
||||||
import org.tasks.time.DateTime
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@UninstallModules(ProductionModule::class)
|
@UninstallModules(ProductionModule::class)
|
||||||
@HiltAndroidTest
|
@HiltAndroidTest
|
||||||
class RepeatWithSubtasksTests : InjectingTestCase() {
|
class RepeatWithSubtasksTests : InjectingTestCase() {
|
||||||
@Inject lateinit var taskDao: TaskDao
|
@Inject lateinit var taskDao: TaskDao
|
||||||
@Inject lateinit var repeat: RepeatTaskHelper
|
@Inject lateinit var taskCompleter: TaskCompleter
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun uncompleteGrandchildren() = runBlocking {
|
fun uncompleteGrandchildren() = runBlocking {
|
||||||
val grandparent = taskDao.createNew(newTask(with(RECUR, "RRULE:FREQ=DAILY")))
|
val grandparent = taskDao.createNew(
|
||||||
val parent = taskDao.createNew(newTask(with(PARENT, grandparent)))
|
Task(
|
||||||
val child = taskDao.createNew(newTask(
|
recurrence = "RRULE:FREQ=DAILY"
|
||||||
with(PARENT, parent),
|
)
|
||||||
with(COMPLETION_TIME, DateTime())
|
)
|
||||||
))
|
val parent = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = grandparent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
repeat.handleRepeat(taskDao.fetch(grandparent)!!)
|
assertTrue(taskDao.fetch(child)!!.isCompleted)
|
||||||
|
|
||||||
|
taskCompleter.setComplete(grandparent)
|
||||||
|
|
||||||
assertFalse(taskDao.fetch(child)!!.isCompleted)
|
assertFalse(taskDao.fetch(child)!!.isCompleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun uncompleteGoogleTaskChildren() = runBlocking {
|
fun uncompleteGoogleTaskChildren() = runBlocking {
|
||||||
val parent = taskDao.createNew(newTask(with(RECUR, "RRULE:FREQ=DAILY")))
|
val parent = taskDao.createNew(
|
||||||
val child = taskDao.createNew(newTask(
|
Task(
|
||||||
with(PARENT, parent),
|
recurrence = "RRULE:FREQ=DAILY"
|
||||||
with(COMPLETION_TIME, DateTime())
|
)
|
||||||
))
|
)
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(taskDao.fetch(child)!!.isCompleted)
|
||||||
|
|
||||||
repeat.handleRepeat(taskDao.fetch(parent)!!)
|
taskCompleter.setComplete(parent)
|
||||||
|
|
||||||
assertFalse(taskDao.fetch(child)!!.isCompleted)
|
assertFalse(taskDao.fetch(child)!!.isCompleted)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,161 @@
|
|||||||
|
package org.tasks.ui.editviewmodel
|
||||||
|
|
||||||
|
import com.todoroo.astrid.core.BuiltInFilterExposer
|
||||||
|
import org.tasks.data.entity.Task
|
||||||
|
import com.todoroo.astrid.service.TaskDeleter
|
||||||
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
|
import dagger.hilt.android.testing.UninstallModules
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.tasks.LocalBroadcastManager
|
||||||
|
import org.tasks.analytics.Firebase
|
||||||
|
import org.tasks.billing.Inventory
|
||||||
|
import org.tasks.data.dao.DeletionDao
|
||||||
|
import org.tasks.data.dao.TaskDao
|
||||||
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.ProductionModule
|
||||||
|
import org.tasks.preferences.Preferences
|
||||||
|
import org.tasks.time.DateTimeUtils2.currentTimeMillis
|
||||||
|
import org.tasks.ui.TaskListViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@UninstallModules(ProductionModule::class)
|
||||||
|
@HiltAndroidTest
|
||||||
|
class TaskListViewModelTest : InjectingTestCase() {
|
||||||
|
private lateinit var viewModel: TaskListViewModel
|
||||||
|
@Inject lateinit var preferences: Preferences
|
||||||
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
@Inject lateinit var taskDeleter: TaskDeleter
|
||||||
|
@Inject lateinit var deletionDao: DeletionDao
|
||||||
|
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
||||||
|
@Inject lateinit var inventory: Inventory
|
||||||
|
@Inject lateinit var firebase: Firebase
|
||||||
|
|
||||||
|
@Before
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
viewModel = TaskListViewModel(
|
||||||
|
context = context,
|
||||||
|
preferences = preferences,
|
||||||
|
taskDao = taskDao,
|
||||||
|
deletionDao = deletionDao,
|
||||||
|
taskDeleter = taskDeleter,
|
||||||
|
localBroadcastManager = localBroadcastManager,
|
||||||
|
inventory = inventory,
|
||||||
|
firebase = firebase,
|
||||||
|
)
|
||||||
|
viewModel.setFilter(BuiltInFilterExposer.getMyTasksFilter(context.resources))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clearCompletedTask() = runBlocking {
|
||||||
|
val task = taskDao.createNew(
|
||||||
|
Task(completionDate = currentTimeMillis())
|
||||||
|
)
|
||||||
|
|
||||||
|
clearCompleted()
|
||||||
|
|
||||||
|
assertTrue(taskDao.fetch(task)!!.isDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontDeleteTaskWithRecurringParent() = runBlocking {
|
||||||
|
val parent = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
recurrence = "RRULE:FREQ=DAILY;INTERVAL=1"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
clearCompleted()
|
||||||
|
|
||||||
|
assertFalse(taskDao.fetch(child)!!.isDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontDeleteTaskWithRecurringGrandparent() = runBlocking {
|
||||||
|
val grandparent = taskDao.createNew(
|
||||||
|
Task(recurrence = "RRULE:FREQ=DAILY;INTERVAL=1")
|
||||||
|
)
|
||||||
|
val parent = taskDao.createNew(
|
||||||
|
Task(parent = grandparent)
|
||||||
|
)
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
clearCompleted()
|
||||||
|
|
||||||
|
assertFalse(taskDao.fetch(child)!!.isDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clearGrandchildWithNoRecurringAncestors() = runBlocking {
|
||||||
|
val grandparent = taskDao.createNew(Task())
|
||||||
|
val parent = taskDao.createNew(
|
||||||
|
Task(parent = grandparent)
|
||||||
|
)
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
clearCompleted()
|
||||||
|
|
||||||
|
assertTrue(taskDao.fetch(child)!!.isDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clearGrandchildWithCompletedRecurringAncestor() = runBlocking {
|
||||||
|
val grandparent = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
recurrence = "RRULE:FREQ=DAILY;INTERVAL=1",
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val parent = taskDao.createNew(
|
||||||
|
Task(parent = grandparent)
|
||||||
|
)
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
clearCompleted()
|
||||||
|
|
||||||
|
assertTrue(taskDao.fetch(child)!!.isDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clearHiddenSubtask() = runBlocking {
|
||||||
|
preferences.showCompleted = false
|
||||||
|
val parent = taskDao.createNew(Task())
|
||||||
|
val child = taskDao.createNew(
|
||||||
|
Task(
|
||||||
|
parent = parent,
|
||||||
|
completionDate = currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
clearCompleted()
|
||||||
|
|
||||||
|
assertTrue(taskDao.fetch(child)!!.isDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun clearCompleted() = viewModel.markDeleted(viewModel.getTasksToClear())
|
||||||
|
}
|
@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.andlib.utility;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.tasks.BuildConfig;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Android Utility Classes
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*/
|
|
||||||
public class AndroidUtilities {
|
|
||||||
|
|
||||||
public static final String SEPARATOR_ESCAPE = "!PIPE!"; // $NON-NLS-1$
|
|
||||||
public static final String SERIALIZATION_SEPARATOR = "|"; // $NON-NLS-1$
|
|
||||||
|
|
||||||
// --- utility methods
|
|
||||||
|
|
||||||
/** Suppress virtual keyboard until user's first tap */
|
|
||||||
public static void suppressVirtualKeyboard(final TextView editor) {
|
|
||||||
final int inputType = editor.getInputType();
|
|
||||||
editor.setInputType(InputType.TYPE_NULL);
|
|
||||||
editor.setOnTouchListener(
|
|
||||||
(v, event) -> {
|
|
||||||
editor.setInputType(inputType);
|
|
||||||
editor.setOnTouchListener(null);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- serialization
|
|
||||||
|
|
||||||
/** Serializes a content value into a string */
|
|
||||||
public static String mapToSerializedString(Map<String, Object> source) {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
for (Entry<String, Object> entry : source.entrySet()) {
|
|
||||||
addSerialized(result, entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** add serialized helper */
|
|
||||||
private static void addSerialized(StringBuilder result, String key, Object value) {
|
|
||||||
result
|
|
||||||
.append(key.replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE))
|
|
||||||
.append(SERIALIZATION_SEPARATOR);
|
|
||||||
if (value instanceof Integer) {
|
|
||||||
result.append('i').append(value);
|
|
||||||
} else if (value instanceof Double) {
|
|
||||||
result.append('d').append(value);
|
|
||||||
} else if (value instanceof Long) {
|
|
||||||
result.append('l').append(value);
|
|
||||||
} else if (value instanceof String) {
|
|
||||||
result
|
|
||||||
.append('s')
|
|
||||||
.append(value.toString().replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE));
|
|
||||||
} else if (value instanceof Boolean) {
|
|
||||||
result.append('b').append(value);
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException(value.getClass().toString());
|
|
||||||
}
|
|
||||||
result.append(SERIALIZATION_SEPARATOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, Serializable> mapFromSerializedString(String string) {
|
|
||||||
if (string == null) {
|
|
||||||
return new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Serializable> result = new HashMap<>();
|
|
||||||
fromSerialized(
|
|
||||||
string,
|
|
||||||
result,
|
|
||||||
(object, key, type, value) -> {
|
|
||||||
switch (type) {
|
|
||||||
case 'i':
|
|
||||||
object.put(key, Integer.parseInt(value));
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
object.put(key, Double.parseDouble(value));
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
object.put(key, Long.parseLong(value));
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
object.put(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR));
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
object.put(key, Boolean.parseBoolean(value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> void fromSerialized(String string, T object, SerializedPut<T> putter) {
|
|
||||||
String[] pairs = string.split("\\" + SERIALIZATION_SEPARATOR); // $NON-NLS-1$
|
|
||||||
for (int i = 0; i < pairs.length; i += 2) {
|
|
||||||
try {
|
|
||||||
String key = pairs[i].replaceAll(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR);
|
|
||||||
String value = pairs[i + 1].substring(1);
|
|
||||||
try {
|
|
||||||
putter.put(object, key, pairs[i + 1].charAt(0), value);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// failed parse to number
|
|
||||||
putter.put(object, key, 's', value);
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
} catch (IndexOutOfBoundsException e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int convertDpToPixels(DisplayMetrics displayMetrics, int dp) {
|
|
||||||
// developer.android.com/guide/practices/screens_support.html#dips-pels
|
|
||||||
return (int) (dp * displayMetrics.density + 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean preOreo() {
|
|
||||||
return !atLeastOreo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean preTiramisu() {
|
|
||||||
return VERSION.SDK_INT < VERSION_CODES.TIRAMISU;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean preUpsideDownCake() {
|
|
||||||
return VERSION.SDK_INT <= VERSION_CODES.TIRAMISU;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastNougatMR1() {
|
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastOreo() {
|
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastP() {
|
|
||||||
return VERSION.SDK_INT >= Build.VERSION_CODES.P;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastQ() {
|
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastR() {
|
|
||||||
return VERSION.SDK_INT >= VERSION_CODES.R;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastS() {
|
|
||||||
return VERSION.SDK_INT >= VERSION_CODES.S;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean atLeastTiramisu() {
|
|
||||||
return VERSION.SDK_INT >= VERSION_CODES.TIRAMISU;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void assertMainThread() {
|
|
||||||
if (BuildConfig.DEBUG && !isMainThread()) {
|
|
||||||
throw new IllegalStateException("Should be called from main thread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void assertNotMainThread() {
|
|
||||||
if (BuildConfig.DEBUG && isMainThread()) {
|
|
||||||
throw new IllegalStateException("Should not be called from main thread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isMainThread() {
|
|
||||||
return Thread.currentThread() == Looper.getMainLooper().getThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SerializedPut<T> {
|
|
||||||
|
|
||||||
void put(T object, String key, char type, String value) throws NumberFormatException;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Todoroo Inc
|
||||||
|
*
|
||||||
|
* See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package com.todoroo.andlib.utility
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import android.os.Looper
|
||||||
|
import android.text.InputType
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import org.tasks.BuildConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android Utility Classes
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim></tim>@todoroo.com>
|
||||||
|
*/
|
||||||
|
object AndroidUtilities {
|
||||||
|
// --- utility methods
|
||||||
|
/** Suppress virtual keyboard until user's first tap */
|
||||||
|
@JvmStatic
|
||||||
|
fun suppressVirtualKeyboard(editor: TextView) {
|
||||||
|
val inputType = editor.inputType
|
||||||
|
editor.inputType = InputType.TYPE_NULL
|
||||||
|
editor.setOnTouchListener { v: View?, event: MotionEvent? ->
|
||||||
|
editor.inputType = inputType
|
||||||
|
editor.setOnTouchListener(null)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertDpToPixels(displayMetrics: DisplayMetrics, dp: Int): Int {
|
||||||
|
// developer.android.com/guide/practices/screens_support.html#dips-pels
|
||||||
|
return (dp * displayMetrics.density + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun preOreo(): Boolean {
|
||||||
|
return !atLeastOreo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun preS(): Boolean {
|
||||||
|
return !atLeastS()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun preTiramisu(): Boolean {
|
||||||
|
return !atLeastTiramisu()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun preUpsideDownCake(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT <= VERSION_CODES.TIRAMISU
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atLeastNougatMR1(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.N_MR1
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun atLeastOreo(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.O
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atLeastOreoMR1(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.O_MR1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atLeastP(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.P
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun atLeastQ(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.Q
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun atLeastR(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.R
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun atLeastS(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.S
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun atLeastTiramisu(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertMainThread() {
|
||||||
|
check(!(BuildConfig.DEBUG && !isMainThread)) { "Should be called from main thread" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertNotMainThread() {
|
||||||
|
check(!(BuildConfig.DEBUG && isMainThread)) { "Should not be called from main thread" }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val isMainThread: Boolean
|
||||||
|
get() = Thread.currentThread() === Looper.getMainLooper().thread
|
||||||
|
}
|
@ -0,0 +1,238 @@
|
|||||||
|
package com.todoroo.astrid.activity
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.todoroo.astrid.activity.MainActivity.Companion.LOAD_FILTER
|
||||||
|
import com.todoroo.astrid.activity.MainActivity.Companion.OPEN_FILTER
|
||||||
|
import com.todoroo.astrid.api.CustomFilter
|
||||||
|
import org.tasks.filters.GtasksFilter
|
||||||
|
import org.tasks.filters.TagFilter
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.tasks.LocalBroadcastManager
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.Tasks.Companion.IS_GENERIC
|
||||||
|
import org.tasks.billing.Inventory
|
||||||
|
import org.tasks.compose.drawer.DrawerItem
|
||||||
|
import org.tasks.data.NO_COUNT
|
||||||
|
import org.tasks.data.count
|
||||||
|
import org.tasks.data.dao.CaldavDao
|
||||||
|
import org.tasks.data.dao.TaskDao
|
||||||
|
import org.tasks.data.entity.Task
|
||||||
|
import org.tasks.filters.CaldavFilter
|
||||||
|
import org.tasks.filters.Filter
|
||||||
|
import org.tasks.filters.FilterProvider
|
||||||
|
import org.tasks.filters.NavigationDrawerSubheader
|
||||||
|
import org.tasks.filters.PlaceFilter
|
||||||
|
import org.tasks.preferences.DefaultFilterProvider
|
||||||
|
import org.tasks.preferences.Preferences
|
||||||
|
import org.tasks.themes.ColorProvider
|
||||||
|
import org.tasks.themes.CustomIcons
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class MainActivityViewModel @Inject constructor(
|
||||||
|
savedStateHandle: SavedStateHandle,
|
||||||
|
private val defaultFilterProvider: DefaultFilterProvider,
|
||||||
|
private val filterProvider: FilterProvider,
|
||||||
|
private val taskDao: TaskDao,
|
||||||
|
private val localBroadcastManager: LocalBroadcastManager,
|
||||||
|
private val inventory: Inventory,
|
||||||
|
private val colorProvider: ColorProvider,
|
||||||
|
private val caldavDao: CaldavDao,
|
||||||
|
private val preferences: Preferences,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
data class State(
|
||||||
|
val begForMoney: Boolean = false,
|
||||||
|
val filter: Filter,
|
||||||
|
val task: Task? = null,
|
||||||
|
val drawerOpen: Boolean = false,
|
||||||
|
val drawerItems: ImmutableList<DrawerItem> = persistentListOf(),
|
||||||
|
val searchItems: ImmutableList<DrawerItem> = persistentListOf(),
|
||||||
|
val menuQuery: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(
|
||||||
|
State(
|
||||||
|
filter = savedStateHandle.get<Filter>(OPEN_FILTER)
|
||||||
|
?: savedStateHandle.get<String>(LOAD_FILTER)?.let {
|
||||||
|
runBlocking { defaultFilterProvider.getFilterFromPreference(it) }
|
||||||
|
}
|
||||||
|
?: runBlocking { defaultFilterProvider.getStartupFilter() },
|
||||||
|
begForMoney = if (IS_GENERIC) !inventory.hasTasksAccount else !inventory.hasPro,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
|
private val refreshReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
when (intent?.action) {
|
||||||
|
LocalBroadcastManager.REFRESH,
|
||||||
|
LocalBroadcastManager.REFRESH_LIST -> updateFilters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resetFilter() {
|
||||||
|
setFilter(defaultFilterProvider.getDefaultOpenFilter())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFilter(
|
||||||
|
filter: Filter,
|
||||||
|
task: Task? = null,
|
||||||
|
) {
|
||||||
|
if (filter == _state.value.filter && task == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
filter = filter,
|
||||||
|
task = task,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
updateFilters()
|
||||||
|
defaultFilterProvider.setLastViewedFilter(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDrawerOpen(open: Boolean) {
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
drawerOpen = open,
|
||||||
|
menuQuery = if (!open) "" else it.menuQuery,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
localBroadcastManager.registerRefreshListReceiver(refreshReceiver)
|
||||||
|
updateFilters()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
localBroadcastManager.unregisterReceiver(refreshReceiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateFilters() = viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
val selected = state.value.filter
|
||||||
|
filterProvider
|
||||||
|
.drawerItems()
|
||||||
|
.map { item ->
|
||||||
|
when (item) {
|
||||||
|
is Filter ->
|
||||||
|
DrawerItem.Filter(
|
||||||
|
title = item.title ?: "",
|
||||||
|
icon = getIcon(item),
|
||||||
|
color = getColor(item),
|
||||||
|
count = item.count.takeIf { it != NO_COUNT } ?: try {
|
||||||
|
taskDao.count(item)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
0
|
||||||
|
},
|
||||||
|
selected = item.areItemsTheSame(selected),
|
||||||
|
shareCount = if (item is CaldavFilter) item.principals else 0,
|
||||||
|
type = { item },
|
||||||
|
)
|
||||||
|
is NavigationDrawerSubheader ->
|
||||||
|
DrawerItem.Header(
|
||||||
|
title = item.title ?: "",
|
||||||
|
collapsed = item.isCollapsed,
|
||||||
|
hasError = item.error,
|
||||||
|
canAdd = item.addIntentRc != 0,
|
||||||
|
type = { item },
|
||||||
|
)
|
||||||
|
else -> throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.let { filters -> _state.update { it.copy(drawerItems = filters.toPersistentList()) } }
|
||||||
|
val query = _state.value.menuQuery
|
||||||
|
filterProvider
|
||||||
|
.allFilters()
|
||||||
|
.filter { it.title!!.contains(query, ignoreCase = true) }
|
||||||
|
.map { item ->
|
||||||
|
DrawerItem.Filter(
|
||||||
|
title = item.title ?: "",
|
||||||
|
icon = getIcon(item),
|
||||||
|
color = getColor(item),
|
||||||
|
count = item.count.takeIf { it != NO_COUNT } ?: try {
|
||||||
|
taskDao.count(item)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
0
|
||||||
|
},
|
||||||
|
selected = item.areItemsTheSame(selected),
|
||||||
|
shareCount = if (item is CaldavFilter) item.principals else 0,
|
||||||
|
type = { item },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.let { filters -> _state.update { it.copy(searchItems = filters.toPersistentList()) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getColor(filter: Filter): Int {
|
||||||
|
if (filter.tint != 0) {
|
||||||
|
val color = colorProvider.getThemeColor(filter.tint, true)
|
||||||
|
if (color.isFree || inventory.purchasedThemes()) {
|
||||||
|
return color.primaryColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIcon(filter: Filter): Int {
|
||||||
|
if (filter.icon < 1000 || filter.icon == CustomIcons.PLACE || inventory.hasPro) {
|
||||||
|
val icon = CustomIcons.getIconResId(filter.icon)
|
||||||
|
if (icon != null) {
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return when (filter) {
|
||||||
|
is TagFilter -> R.drawable.ic_outline_label_24px
|
||||||
|
is GtasksFilter,
|
||||||
|
is CaldavFilter -> R.drawable.ic_list_24px
|
||||||
|
|
||||||
|
is CustomFilter -> R.drawable.ic_outline_filter_list_24px
|
||||||
|
is PlaceFilter -> R.drawable.ic_outline_place_24px
|
||||||
|
else -> filter.icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleCollapsed(subheader: NavigationDrawerSubheader) = viewModelScope.launch {
|
||||||
|
val collapsed = !subheader.isCollapsed
|
||||||
|
when (subheader.subheaderType) {
|
||||||
|
NavigationDrawerSubheader.SubheaderType.PREFERENCE -> {
|
||||||
|
preferences.setBoolean(subheader.id.toInt(), collapsed)
|
||||||
|
localBroadcastManager.broadcastRefreshList()
|
||||||
|
}
|
||||||
|
NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS,
|
||||||
|
NavigationDrawerSubheader.SubheaderType.CALDAV,
|
||||||
|
NavigationDrawerSubheader.SubheaderType.TASKS,
|
||||||
|
NavigationDrawerSubheader.SubheaderType.ETESYNC -> {
|
||||||
|
caldavDao.setCollapsed(subheader.id, collapsed)
|
||||||
|
localBroadcastManager.broadcastRefreshList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTask(task: Task?) {
|
||||||
|
_state.update { it.copy(task = task) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queryMenu(query: String) {
|
||||||
|
_state.update { it.copy(menuQuery = query) }
|
||||||
|
updateFilters()
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
package com.todoroo.astrid.adapter
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import org.tasks.databinding.FilterAdapterActionBinding
|
|
||||||
import org.tasks.filters.NavigationDrawerAction
|
|
||||||
import org.tasks.themes.DrawableUtil
|
|
||||||
|
|
||||||
class ActionViewHolder internal constructor(
|
|
||||||
private val context: Context,
|
|
||||||
itemView: View,
|
|
||||||
private val onClick: (NavigationDrawerAction) -> Unit
|
|
||||||
) : RecyclerView.ViewHolder(itemView) {
|
|
||||||
|
|
||||||
private val row: View
|
|
||||||
private val text: TextView
|
|
||||||
private val icon: ImageView
|
|
||||||
|
|
||||||
init {
|
|
||||||
FilterAdapterActionBinding.bind(itemView).let {
|
|
||||||
row = it.row
|
|
||||||
text = it.text
|
|
||||||
icon = it.icon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bind(filter: NavigationDrawerAction) {
|
|
||||||
text.text = filter.title
|
|
||||||
icon.setImageDrawable(DrawableUtil.getWrapped(context, filter.icon))
|
|
||||||
row.setOnClickListener {
|
|
||||||
onClick.invoke(filter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package com.todoroo.astrid.adapter
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
|
|
||||||
class SeparatorViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView)
|
|
@ -1,57 +0,0 @@
|
|||||||
package com.todoroo.astrid.api
|
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
|
|
||||||
interface Filter : FilterListItem, Parcelable {
|
|
||||||
val valuesForNewTasks: String?
|
|
||||||
get() = null
|
|
||||||
val sql: String?
|
|
||||||
val icon: Int
|
|
||||||
get() = -1
|
|
||||||
val title: String?
|
|
||||||
val tint: Int
|
|
||||||
get() = 0
|
|
||||||
@Deprecated("Remove this")
|
|
||||||
var count: Int
|
|
||||||
val order: Int
|
|
||||||
get() = NO_ORDER
|
|
||||||
override val itemType: FilterListItem.Type
|
|
||||||
get() = FilterListItem.Type.ITEM
|
|
||||||
val isReadOnly: Boolean
|
|
||||||
get() = false
|
|
||||||
val isWritable: Boolean
|
|
||||||
get() = !isReadOnly
|
|
||||||
|
|
||||||
fun supportsManualSort(): Boolean = false
|
|
||||||
fun supportsHiddenTasks(): Boolean = true
|
|
||||||
fun supportsSubtasks(): Boolean = true
|
|
||||||
fun supportsSorting(): Boolean = true
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val NO_ORDER = -1
|
|
||||||
const val NO_COUNT = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Use manual ordering")
|
|
||||||
interface AstridOrderingFilter : Filter {
|
|
||||||
var filterOverride: String?
|
|
||||||
|
|
||||||
fun getSqlQuery(): String = filterOverride ?: sql!!
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class FilterImpl(
|
|
||||||
override val title: String? = null,
|
|
||||||
override val sql: String? = null,
|
|
||||||
override val valuesForNewTasks: String? = null,
|
|
||||||
override val icon: Int = -1,
|
|
||||||
override val tint: Int = 0,
|
|
||||||
override var count: Int = NO_COUNT,
|
|
||||||
) : Filter {
|
|
||||||
override fun areItemsTheSame(other: FilterListItem): Boolean {
|
|
||||||
return other is Filter && sql == other.sql
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package com.todoroo.astrid.api
|
|
||||||
|
|
||||||
import androidx.annotation.LayoutRes
|
|
||||||
import org.tasks.R
|
|
||||||
|
|
||||||
interface FilterListItem {
|
|
||||||
val itemType: Type
|
|
||||||
|
|
||||||
fun areItemsTheSame(other: FilterListItem): Boolean
|
|
||||||
|
|
||||||
enum class Type(@param:LayoutRes val layout: Int) {
|
|
||||||
ITEM(R.layout.filter_adapter_row),
|
|
||||||
ACTION(R.layout.filter_adapter_action),
|
|
||||||
SUBHEADER(R.layout.filter_adapter_subheader),
|
|
||||||
SEPARATOR(R.layout.filter_adapter_separator)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package com.todoroo.astrid.dao
|
|
||||||
|
|
||||||
import androidx.room.AutoMigration
|
|
||||||
import androidx.room.Database
|
|
||||||
import androidx.room.RoomDatabase
|
|
||||||
import com.todoroo.astrid.data.Task
|
|
||||||
import org.tasks.data.*
|
|
||||||
import org.tasks.data.TaskDao
|
|
||||||
import org.tasks.db.Migrations
|
|
||||||
import org.tasks.notifications.Notification
|
|
||||||
import org.tasks.notifications.NotificationDao
|
|
||||||
|
|
||||||
@Database(
|
|
||||||
entities = [
|
|
||||||
Notification::class,
|
|
||||||
TagData::class,
|
|
||||||
UserActivity::class,
|
|
||||||
TaskAttachment::class,
|
|
||||||
TaskListMetadata::class,
|
|
||||||
Task::class,
|
|
||||||
Alarm::class,
|
|
||||||
Place::class,
|
|
||||||
Geofence::class,
|
|
||||||
Tag::class,
|
|
||||||
Filter::class,
|
|
||||||
CaldavCalendar::class,
|
|
||||||
CaldavTask::class,
|
|
||||||
CaldavAccount::class,
|
|
||||||
Principal::class,
|
|
||||||
PrincipalAccess::class,
|
|
||||||
Attachment::class,
|
|
||||||
],
|
|
||||||
autoMigrations = [
|
|
||||||
AutoMigration(from = 83, to = 84, spec = Migrations.AutoMigrate83to84::class),
|
|
||||||
],
|
|
||||||
version = 88
|
|
||||||
)
|
|
||||||
abstract class Database : RoomDatabase() {
|
|
||||||
abstract fun notificationDao(): NotificationDao
|
|
||||||
abstract val tagDataDao: TagDataDao
|
|
||||||
abstract val userActivityDao: UserActivityDao
|
|
||||||
abstract val taskAttachmentDao: TaskAttachmentDao
|
|
||||||
abstract val taskListMetadataDao: TaskListMetadataDao
|
|
||||||
abstract val alarmDao: AlarmDao
|
|
||||||
abstract val locationDao: LocationDao
|
|
||||||
abstract val tagDao: TagDao
|
|
||||||
abstract val googleTaskDao: GoogleTaskDao
|
|
||||||
abstract val filterDao: FilterDao
|
|
||||||
abstract val googleTaskListDao: GoogleTaskListDao
|
|
||||||
abstract val taskDao: TaskDao
|
|
||||||
abstract val caldavDao: CaldavDao
|
|
||||||
abstract val deletionDao: DeletionDao
|
|
||||||
abstract val contentProviderDao: ContentProviderDao
|
|
||||||
abstract val upgraderDao: UpgraderDao
|
|
||||||
abstract val principalDao: PrincipalDao
|
|
||||||
|
|
||||||
/** @return human-readable database name for debugging
|
|
||||||
*/
|
|
||||||
override fun toString(): String {
|
|
||||||
return "DB:$name"
|
|
||||||
}
|
|
||||||
|
|
||||||
val name: String
|
|
||||||
get() = NAME
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val NAME = "database"
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue