Compare commits

...

215 Commits
13.7 ... main

Author SHA1 Message Date
renovate[bot] f8bb045d76 chore(deps): update actions/cache action to v4 2 days ago
renovate[bot] 4040a2379b fix(deps): update dependency com.google.android.gms:play-services-oss-licenses to v17.1.0 2 days ago
Alex Baker c4ee7479ca
Cache avd 2 days ago
Alex Baker be861597ef Merge branch '13.9.8' 2 days ago
Alex Baker f27332595d Set root project name for project accessors 3 days ago
Alex Baker ea7f051d85 Revert "Restore @Transaction annotations"
This reverts commit b35090cd43.
3 days ago
Alex Baker 8be7fab033 Update version and changelog 3 days ago
Alex Baker d6e0c0bdcf Fix showing completed tasks in subtask filter 3 days ago
Alex Baker 5ec02011f8 Fix backup import crashes 3 days ago
Alex Baker b35090cd43 Restore @Transaction annotations 3 days ago
renovate[bot] 92f62450ae fix(deps): update accompanist to v0.34.0 3 days ago
renovate[bot] 3ca6912492 fix(deps): update dependency androidx.appcompat:appcompat to v1.7.0 3 days ago
renovate[bot] 080b1428dd fix(deps): update room to v2.7.0-alpha03 3 days ago
Alex Baker f67c3bc56c Enable typesafe project accessors 3 days ago
renovate[bot] 5d0e88a620 fix(deps): update dependency com.google.android.gms:play-services-location to v21.3.0 3 days ago
renovate[bot] c5d5795fe2 fix(deps): update lifecycle to v2.8.1 3 days ago
renovate[bot] 3bbc0e0ab0 fix(deps): update dependency androidx.sqlite:sqlite-bundled to v2.5.0-alpha03 3 days ago
Alex Baker 009a195580 Update plugin definitions 3 days ago
renovate[bot] 772f69d8c0 fix(deps): update dependency com.google.apis:google-api-services-drive to v3-rev20240521-2.0.0 4 days ago
renovate[bot] 4229bf7067 fix(deps): update dependency com.google.apis:google-api-services-tasks to v1-rev20240526-2.0.0 4 days ago
Alex Baker 212a4b0a3d Delete transaction check
This was using platform sqlite
4 days ago
Alex Baker 4ddfe937b0 Finish converting data module to kmp 5 days ago
Alex Baker 19de0e08a5 Migrate to bundled sqlite 5 days ago
Alex Baker 60211355e0 Remove Geofence constructors 6 days ago
Alex Baker 17d218aa4e Add CommonParcelize 6 days ago
Alex Baker 505c8c29d5 Make sure dao methods are suspending 6 days ago
Alex Baker 7149308c97 Move Android platform stuff out of data 6 days ago
Alex Baker 2c5a497007 Fix backup import crash 1 week ago
Alex Baker 09f53fe1e5 Ignore multiplatform agp warnings 1 week ago
Alex Baker 5da4183aed Move ksp to gradle catalog 1 week ago
Alex Baker d35912e503 Kotlin 2.0 1 week ago
renovate[bot] 82fd99f83e fix(deps): update lifecycle to v2.8.0 1 week ago
renovate[bot] f944becea1 fix(deps): update dependency com.google.apis:google-api-services-drive to v3-rev20240509-2.0.0 1 week ago
Alex Baker acd713dc5b AGP 8.4.1 1 week ago
Alex Baker 1a93c87ad9 Update version and changelog 1 week ago
Alex Baker c4e25b8b15 Fix tests 1 week ago
Alex Baker e11c0d2528 Add default reminders when adding due/start date 1 week ago
Alex Baker 2fc6833854 Don't crash on missing vtodo value 1 week ago
Alex Baker 4a2fb13d10 Converting data module to kmp - WIP 2 weeks ago
Alex Baker a2572e2dee Remove CaldavCalendarMaker 2 weeks ago
Alex Baker 64e05c9f8f Convert Tag to data class 2 weeks ago
Alex Baker ad833b5f49 Convert TagData to data class 2 weeks ago
Alex Baker eea944cc7b Update version and changelog 2 weeks ago
Alex Baker c82dfc7d39 Fix test? 2 weeks ago
Alex Baker 8607f9556a Fix test 2 weeks ago
Alex Baker f338e84d46 Fix warnings in Migrations 2 weeks ago
Alex Baker 9ee739627e Remove AlarmEntry 2 weeks ago
Alex Baker a49c233584 Make notification immutable 2 weeks ago
Alex Baker 74fca07c1b Make alarm immutable 2 weeks ago
Alex Baker 5bd0cef42e Remove extra alarm constructor 2 weeks ago
Alex Baker 4c245edbb4 Fix snooze causing duplicate notifications 2 weeks ago
Alex Baker 97a3f074d0 Update alarms after completion transaction 2 weeks ago
Alex Baker 86ecd3cf81 Synchronize alarms before saving 2 weeks ago
Alex Baker 07a2eda5ea Cancel notifications in TaskCompleter 2 weeks ago
renovate[bot] 09ffbdd036 fix(deps): update dependency com.google.firebase:firebase-crashlytics-gradle to v3 2 weeks ago
renovate[bot] 60f22146ca fix(deps): update dependency androidx.fragment:fragment-ktx to v1.7.1 2 weeks ago
renovate[bot] c11225abaf fix(deps): update kotlin 2 weeks ago
dependabot[bot] 133ea493e3 Bump rexml from 3.2.6 to 3.2.8
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.6 to 3.2.8.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.6...v3.2.8)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2 weeks ago
Alex Baker 0ba901be69 Remove livedata from data module 3 weeks ago
Alex Baker ebe5e5c009 Replace gson with kotlin serialization 3 weeks ago
Alex Baker d556863fda Use kotlin serialization for backups 3 weeks ago
Alex Baker 55adbc2025 Reorganized data module 3 weeks ago
Alex Baker 06c4255886 Remove androidx.core from data module 3 weeks ago
renovate[bot] 4734a99bae fix(deps): update mockito monorepo to v5.12.0 3 weeks ago
Alex Baker a6a8cac8e4 Update dependencies 3 weeks ago
Alex Baker c3fc9a57cc Replace now with currentTimeMillis 3 weeks ago
Alex Baker 6e14d07d0c Move Room to data module 3 weeks ago
Alex Baker 6118121698 Moving some code out of TimerPlugin 3 weeks ago
Alex Baker 6bf3bd4d08 Update version and changelog 3 weeks ago
Alex Baker 065be79355 Update notification work logic 3 weeks ago
Alex Baker f8f8ba3c51 Don't adjust random reminder time 3 weeks ago
Alex Baker 89465f36b3 Update version and changelog 3 weeks ago
Alex Baker 1380a34ffa Fix alarm test 3 weeks ago
Alex Baker 10af5280a3 Fix random reminders 3 weeks ago
Alex Baker 8c0f7b952d ForegroundInfo for expedited work on Android 11- 3 weeks ago
Alex Baker 65362b203f Update version and changelog 3 weeks ago
Alex Baker 3327f97a17 Revert change to not delete evicted notifications 3 weeks ago
Alex Baker c9fc02a42e Enable room kotlin codegen 4 weeks ago
renovate[bot] 93670bb9e4 fix(deps): update dependency com.google.firebase:firebase-bom to v33 4 weeks ago
Alex Baker 1fc6a50d0b Update version and changelog 4 weeks ago
Alex Baker e1ef924909 Revert "Load initial data in task edit view model"
This reverts commit b2efb42d55.
4 weeks ago
Alex Baker 686cb5d346 Add empty filter 4 weeks ago
renovate[bot] ebec25c4cb fix(deps): update dependency com.google.android.material:material to v1.12.0 4 weeks ago
renovate[bot] c140f7e673
fix(deps): update dependency com.squareup.leakcanary:leakcanary-android to v2.14 4 weeks ago
renovate[bot] efbcf11a4a fix(deps): update dependency com.google.apis:google-api-services-drive to v3-rev20240327-2.0.0 4 weeks ago
renovate[bot] 6adee85a37 fix(deps): update dependency com.google.apis:google-api-services-tasks to v1-rev20240423-2.0.0 4 weeks ago
Alex Baker 5c8643110b Update back press and intent handling 4 weeks ago
Alex Baker abd13aeb75 Exclude META-INF/INDEX.LIST 4 weeks ago
Alex Baker c210fe1893 Fix finishing recurrence 4 weeks ago
Alex Baker 26aa916c20 Fix widget crash 4 weeks ago
renovate[bot] 1eff2d1cd5 fix(deps): update dependency androidx.fragment:fragment-ktx to v1.7.0 4 weeks ago
islam2hamy c90e683ea3
Translated using Weblate (Arabic)
Currently translated at 94.1% (626 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/ar/
4 weeks ago
Alex Baker 3cd0295b71 Refactor notification scheduling
* Remove foreground service
* Use expedited work to trigger notifications
* Remove miscellaneous notification channel
4 weeks ago
Alex Baker 95c351e9fd Remove midnight refresh worker 4 weeks ago
renovate[bot] 4ddb7816f1 fix(deps): update dependency androidx.compose:compose-bom to v2024.05.00 4 weeks ago
renovate[bot] 91c30f7bbf chore(deps): update dependency gradle to v8.7 1 month ago
renovate[bot] 3f4398b6e0 chore(deps): update dependency fastlane to v2.220.0 1 month ago
renovate[bot] c822e989a3 fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.13 1 month ago
renovate[bot] da146723e5 chore(deps): update dependency ruby to v3.3.1 1 month ago
109247019824 931626c84a Translated using Weblate (Bulgarian)
Currently translated at 100.0% (665 of 665 strings)

Co-authored-by: 109247019824 <stoyan@gmx.com>
Translate-URL: https://hosted.weblate.org/projects/tasks/android/bg/
Translation: Tasks.org/Android
1 month ago
Alex Baker c534632c52 Pass uuid to TaskAdapter.onCompletedTask 1 month ago
Alex Baker c1347a7455 Update version and changelog 1 month ago
renovate[bot] 9544909a58 Update dependency androidx.activity:activity-compose to v1.9.0 1 month ago
Yurt Page 5c10dce2b9 fastlane: i18n ru
Signed-off-by: Yurt Page <yurtpage@gmail.com>
1 month ago
Alex Baker 584d4a5cbb Move after update work inside transaction 1 month ago
Alex Baker 7c68a7fa59 AGP 8.4.0 1 month ago
purushottamyadavbattula 215cc838ef Sending local broadcast refresh event for refreshing nav drawer menu to communicate about update events 1 month ago
Alex Baker d60472d1bc Remove RefreshScheduler 1 month ago
Alex Baker f84a37a60a Revert "Replace refresh work with coroutines"
Widgets 😢
1 month ago
Alex Baker 7fb85b6da1 Replace refresh work with coroutines 1 month ago
Alex Baker dc90e583e4 Fix hiding empty items in drawer 1 month ago
Don Zouras 0eac5f61eb Translated using Weblate (Esperanto)
Currently translated at 100.0% (665 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/eo/
1 month ago
Milo Ivir c686ce883d Translated using Weblate (Croatian)
Currently translated at 100.0% (665 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/hr/
1 month ago
大王叫我来巡山 ab25398cd0 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (665 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/zh_Hans/
1 month ago
renovate[bot] 3b1c133d22 Update kotlin 1 month ago
renovate[bot] 3bfd0ab4f8 Update dependency com.google.firebase:firebase-bom to v32.8.1 1 month ago
Liz de Sartiges ffc0113d7f Initial support for z flip 5 cover screen
see : https://developer.samsung.com/galaxy-z/flex_window.html
1 month ago
Don Zouras 9de9718ad5 Translated using Weblate (Esperanto)
Currently translated at 100.0% (665 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/eo/
1 month ago
Oğuz Ersen a7d2c9c406 Translated using Weblate (Turkish)
Currently translated at 100.0% (665 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/tr/
1 month ago
gallegonovato b3006b9ac2 Translated using Weblate (Spanish)
Currently translated at 100.0% (665 of 665 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/es/
1 month ago
Don Zouras de3ef1f9c9 Translated using Weblate (Esperanto)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/eo/
1 month ago
Alex Baker ce9e722a3f Delete more unused tag picker code 1 month ago
Alex Baker 4b892a0eb1 Rename to TagPickerActivity
Delete some more unused code
1 month ago
Hady e6e275834a
Tag picker compose (#2849)
TagPickerActivity refactoring to Compose

1. state of the SearchBar moved to the viewModel
2. viewModel used as parameter to @Composables instead of number of separate ones
3. Import of TagPickerActivity is replaced by TagPickerActivityCompose through the project
1 month ago
Alex Baker 782f4d6d7c Fix swipe to snooze time 1 month ago
elmuffo a1da71d3e1
Swipe to snooze (#2839) 1 month ago
Alex Baker c793a300cc Add preference summary 1 month ago
Ilya Bizyaev bf84bf9e82 [Feature] Add an option to allow adding tasks without unlock
I often find myself picking up the phone just to write down a task, so
I've added a notification drawer quick setting to speed things up.
However, when I use this button from the lock screen, I have to unlock
my device first, which is annoying. I would like to be able to add (not
view) tasks without the need to unlock my phone.

This PR adds such an optional feature for devices running Android 8.1+.
Note that I am not an Android developer, so the implementation is
probably not perfect. However, from my testing on an emulator, this
code seems to do just what I want.
1 month ago
SC 363b29babb
Translated using Weblate (Portuguese)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt/
2 months ago
min7-i c1ff953f5c Translated using Weblate (German)
Currently translated at 99.3% (651 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/de/
2 months ago
Alex Baker 63482e5db9 AGP 8.3.2 2 months ago
Emin Tufan Çetin 2f7dc0c7f1
Translated using Weblate (Turkish)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/tr/
2 months ago
Lionel HANNEQUIN d672507fae
Translated using Weblate (French)
Currently translated at 99.8% (654 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/fr/
2 months ago
Jonatan Nyberg ce2a3c8a3f
Translated using Weblate (Swedish)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/sv/
2 months ago
sorifukobexomajepasiricupuva33 9cd114d68b
Translated using Weblate (German)
Currently translated at 99.2% (650 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/de/
2 months ago
Patrick V. Leguizamon 0e663f0e08
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt_BR/
2 months ago
Mayhm 1d1efd008d
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt_BR/
2 months ago
Alex Baker 26ab3d5866 Exclude past snooze times from Snooze Filter
This should exclude tasks that were completed before their snooze time
lapsed
2 months ago
Mayhm 9a4fcbbd39
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt_BR/
2 months ago
Alex Baker 72bfda9224 Fix subtasks row for new tasks 2 months ago
Alex Baker 1067de4183 Emit SectionedDataSource from TaskListViewModel 2 months ago
Alex Baker d686b8c7e0 Add TasksMenu composable 2 months ago
Alex Baker b2efb42d55 Load initial data in task edit view model 2 months ago
Fabio Parri 3448808c94 Translated using Weblate (Portuguese)
Currently translated at 99.3% (651 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt/
2 months ago
Alex Baker 06a9626052 Update version and changelog 2 months ago
Alex Baker e92ab7f7e1 Update to latest ModalBottomSheet 2 months ago
Alex Baker 4ff7b18c0f Fix cloning google tasks 2 months ago
Alex Baker 91887f6b17 Fix backup import dropping some tasks 2 months ago
Alex Baker cf30b56098 Update version and changelog 2 months ago
Alex Baker 9bcadaab5a Fix astrid manual ordering crash in widget 2 months ago
Alex Baker be766074b0 Fix activity crash 2 months ago
Ihor Hordiichuk 64a42a3f61 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/uk/
2 months ago
Mayhm 7b65ba6f06 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.6% (646 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt_BR/
2 months ago
109247019824 ac2b270e9e Translated using Weblate (Bulgarian)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/bg/
2 months ago
Alex Baker db2ea0a039 Fix import crash on missing remoteId 2 months ago
renovate[bot] 08b78fe9f4 Update dependency androidx.compose:compose-bom to v2024.03.00 2 months ago
Alex Baker 1a1301ae3e Update version and changelog 2 months ago
Milo Ivir d00061aa7f Translated using Weblate (Croatian)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/hr/
2 months ago
大王叫我来巡山 45add6ab32 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/zh_Hans/
2 months ago
Pierfrancesco Passerini af43737c4e Translated using Weblate (Italian)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/it/
2 months ago
macpac59 dd40e59b17 Translated using Weblate (German)
Currently translated at 98.9% (648 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/de/
2 months ago
gallegonovato 13f3248a01 Translated using Weblate (Spanish)
Currently translated at 100.0% (655 of 655 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/es/
2 months ago
renovate[bot] f6972e3e30 Update dependency com.android.tools.build:gradle to v8.3.1 2 months ago
Alex Baker 83cf48a836 Don't pass filter to remoteviews service
This was working on emulators but crashing in the wild
2 months ago
Alex Baker b7b4747a04 Update translation credits
Was in a rush to get a bug fix out!
2 months ago
Alex Baker 6bec2ceef0 Update version and changelog 2 months ago
Milo Ivir d1e60d6512 Translated using Weblate (Croatian)
Currently translated at 100.0% (654 of 654 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/hr/
2 months ago
bittin1ddc447d824349b2 2b85089d3a Translated using Weblate (Swedish)
Currently translated at 100.0% (654 of 654 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/sv/
2 months ago
ferranpujolcamins 2a0ef9feb6 Translated using Weblate (Catalan)
Currently translated at 34.7% (227 of 654 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/ca/
2 months ago
109247019824 33adbbd884 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (654 of 654 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/bg/
2 months ago
Alex Baker c25eb2e0c5 Fix crash on earlier Android versions 2 months ago
Alex Baker 14026356eb Fix widget arrow color 3 months ago
Alex Baker b328651dd4
Run tests on generic flavor (#2808) 3 months ago
Alex Baker a0e9bfabeb Update version and changelog 3 months ago
大王叫我来巡山 a1ad421b33 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (658 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/zh_Hans/
3 months ago
Mayhm 3488a08af1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.4% (648 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/pt_BR/
3 months ago
gallegonovato b71d1af516 Translated using Weblate (Spanish)
Currently translated at 100.0% (658 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/es/
3 months ago
Alex Baker 041dce8617 Add dynamic widget theme 3 months ago
Alex Baker 3d92ca78dd Remove unused attrs 3 months ago
Alex Baker a32fce2d8b Remove widget_title_* layouts 3 months ago
Alex Baker 4fb3cda173 Fix loading selected filter on startup 3 months ago
Alex Baker f33cc896dd Refactor widget 3 months ago
Alex Baker 4d1d6a06a8 Fix repeat until crash 3 months ago
Alex Baker 2202516688 Update isOverdue logic 3 months ago
Alex Baker d4a5008ecb Update CI 3 months ago
Alex Baker 08189e10f1 Don't use gradle managed devices in CI 3 months ago
Anonymous d3e4c066d8 Translated using Weblate (Sinhala)
Currently translated at 92.0% (606 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/si/
3 months ago
Anonymous bbc5ae4d6d Translated using Weblate (Tamil)
Currently translated at 68.2% (449 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/ta/
3 months ago
Anonymous c6cc00cf07 Translated using Weblate (Thai)
Currently translated at 89.5% (589 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/th/
3 months ago
Anonymous 22e8720021 Translated using Weblate (Hebrew)
Currently translated at 89.5% (589 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/he/
3 months ago
Anonymous a3ce98f0ea Translated using Weblate (Danish)
Currently translated at 95.8% (631 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/da/
3 months ago
macpac59 258f607d52 Translated using Weblate (German)
Currently translated at 99.6% (656 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/de/
3 months ago
ngocanhtve 927acae7e4 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (658 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/vi/
3 months ago
Odweta 49ad9bafe3 Translated using Weblate (Czech)
Currently translated at 100.0% (658 of 658 strings)

Translation: Tasks.org/Android
Translate-URL: https://hosted.weblate.org/projects/tasks/android/cs/
3 months ago
Alex Baker 6df616d9ce Use gradle managed devices 3 months ago
Alex Baker 157668e35a Fix tests 3 months ago
Aslam Karachiwala efdf343869 For English (en), replaced "until" with "ends on" in 'repeats_*' keys. 3 months ago
renovate[bot] 5606df17c5 Update flipper to v0.250.0 3 months ago
renovate[bot] fc3b4971f4 Update flipper to v0.249.0 3 months ago
renovate[bot] 6a1699bb33 Update dependency androidx.compose:compose-bom to v2024.02.02 3 months ago
renovate[bot] e49303d5ca Update dependency com.google.firebase:firebase-bom to v32.7.4 3 months ago
renovate[bot] 4b55569b51 Update mockito monorepo to v5.11.0 3 months ago
renovate[bot] 2d7145cde3 Update plugin com.google.devtools.ksp to v1.9.22-1.0.18 3 months ago
renovate[bot] f2ab8bed95 Update dependency com.google.firebase:firebase-bom to v32.7.3 3 months ago
renovate[bot] a5bc4cf536 Update dependency com.android.tools.build:gradle to v8.3.0 3 months ago
renovate[bot] 1b35372b3a Update dependency com.google.apis:google-api-services-tasks to v1-rev20240225-2.0.0 3 months ago
Alex Baker c0fd4bf66a Convert LocalBroadcastManager to Kotlin 3 months ago
renovate[bot] 5d366f0d61 Update dependency io.coil-kt:coil-gif to v2.6.0 3 months ago
renovate[bot] d0635ac6f3 Update hilt to v1.2.0 3 months ago
renovate[bot] 8d4cf4daa5 Update dependency androidx.compose.compiler:compiler to v1.5.10 3 months ago
renovate[bot] d1e439e70e Update dependency androidx.compose:compose-bom to v2024.02.01 3 months ago
renovate[bot] 4d4c3e5193 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.8.0 4 months ago
Alex Baker 20f87061fd Convert WidgetPreferences to Kotlin 4 months ago
renovate[bot] c03e3747c6 Update dependency com.google.gms:google-services to v4.4.1 4 months ago
renovate[bot] 925b1b9124 Update dependency com.google.firebase:firebase-bom to v32.7.2 4 months ago

@ -33,24 +33,59 @@ jobs:
path: app/build/reports/*.html path: app/build/reports/*.html
test: test:
runs-on: macos-latest runs-on: ubuntu-latest
strategy:
matrix:
flavor: [Googleplay, Generic]
api-level: [29]
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '17' java-version: '17'
cache: 'gradle' cache: 'gradle'
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: AVD cache
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.api-level }}
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: echo "Generated AVD snapshot for caching."
- name: run tests - name: run tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
with: with:
api-level: 29 api-level: ${{ matrix.api-level }}
script: ./gradlew -Pcoverage app:testGoogleplayDebugUnitTest app:connectedGoogleplayDebugAndroidTest force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: ./gradlew -Pcoverage app:test${{ matrix.flavor }}DebugUnitTest app:connected${{ matrix.flavor }}DebugAndroidTest
- name: Upload test reports - name: Upload test reports
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: ${{ always() }} if: ${{ always() }}
with: with:
name: test-reports name: test-reports-${{ matrix.flavor }}
path: app/build/reports/** path: app/build/reports/**

1
.gitignore vendored

@ -1,3 +1,4 @@
.kotlin
.idea .idea
*.iml *.iml
.gradle .gradle

@ -1 +1 @@
3.3.0 3.3.1

@ -1,3 +1,77 @@
### 13.9.8 (2024-05-30)
* Fix import backup crashes
* Fix showing completed subtasks in edit screen
### 13.9.7 (2024-05-23)
* Add default reminders when adding start/due dates to existing tasks [#1846](https://github.com/tasks/tasks/issues/1846)
* Fix import backup crash
### 13.9.6 (2024-05-18)
* Fix widget crash [#2873](https://github.com/tasks/tasks/issues/2873)
* Fix recurrence unable to finish [#2874](https://github.com/tasks/tasks/issues/2874)
* Fix edit screen being cleared when reopening app [#2857](https://github.com/tasks/tasks/issues/2857)
* Fix performance regressions
* Simplified internal alarm scheduling logic
* Update translations
* Arabic - @islam2hamy
* Bulgarian - @StoyanDimitrov
### 13.9 (2024-05-01)
* @elmuffo: Add swipe-to-snooze [#2839](https://github.com/tasks/tasks/pull/2839)
* @IlyaBizyaev: Add option to use quick tile without unlocking device [#2847](https://github.com/tasks/tasks/pull/2847)
* @liz-desartiges: Add support for Z Flip 5 cover screen [#2843](https://github.com/tasks/tasks/pull/2843)
* @purushyb: Fix drawer not updating after editing items [#2855](https://github.com/tasks/tasks/pull/2855)
* @hady-exc: Migrate tag picker screen to Compose [#2849](https://github.com/tasks/tasks/pull/2849)
* @yurtpage: Add Russian app store description [#2848](https://github.com/tasks/tasks/pull/2848)
* Fix duplicate notifications [#2835](https://github.com/tasks/tasks/issues/2835)
* Fix adding '(Completed)' to calendar entries [#2832](https://github.com/tasks/tasks/issues/2832)
* Fix hiding empty items from drawer [#2831](https://github.com/tasks/tasks/issues/2831)
* Exclude old snoozed tasks from snoozed task filter
* Update translations
* Brazilian Portuguese - @mayhmemo, @gorgonun
* Chinese (Simplified) - 大王叫我来巡山
* Croatian - @milotype
* Esperanto - Don Zouras
* French - Lionel HANNEQUIN
* German - sorifukobexomajepasiricupuva33, min7-i
* Portuguese - @fparri, @laralem
* Spanish - gallegonovato
* Swedish - @JonatanWick
* Turkish - @emintufan, @oersen
### 13.8.1 (2024-03-24)
* Fix copy causing duplicate Google Tasks
* Fix navigation drawer crash
* Fix backup import dropping tasks
### 13.8 (2024-03-22)
* Dynamic widget theme (name-your-price subscription required)
* Replace 'until' with 'ends on' for repeating tasks [#2797](https://github.com/tasks/tasks/pull/2797) - @akwala
* Fix loading selected list on startup [#2777](https://github.com/tasks/tasks/issues/2777)
* Fix repeating tasks ending one day early
* Fix repeating task crash
* Fix backup import crash
* Fix Astrid manual ordering crash in widget
* Update translations
* Brazilian Portuguese - @mayhmemo
* Bulgarian - @StoyanDimitrov
* Catalan - @ferranpujolcamins
* Chinese (Simplified) - 大王叫我来巡山
* Croatian - @milotype
* Czech - Odweta
* German - @macpac59
* Italian - @ppasserini
* Spanish - gallegonovato
* Swedish - @bittin
* Ukrainian - @IhorHordiichuk
* Vietnamese - @ngocanhtve
### 13.7 (2024-02-07) ### 13.7 (2024-02-07)
* Fix returning to previous filter after search [#2700](https://github.com/tasks/tasks/pull/2700) * Fix returning to previous filter after search [#2700](https://github.com/tasks/tasks/pull/2700)
@ -31,7 +105,7 @@
* Swedish - @bittin * Swedish - @bittin
* Turkish - @oersen * Turkish - @oersen
* Ukrainian - Сергій * Ukrainian - Сергій
* Vietnamese -@ngocanhtve * Vietnamese - @ngocanhtve
### 13.6.3 (2023-11-25) ### 13.6.3 (2023-11-25)

@ -1,29 +1,32 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
CFPropertyList (3.0.6) CFPropertyList (3.0.7)
base64
nkf
rexml rexml
addressable (2.8.6) addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
artifactory (3.0.15) artifactory (3.0.17)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.3.0) aws-eventstream (1.3.0)
aws-partitions (1.877.0) aws-partitions (1.923.0)
aws-sdk-core (3.190.1) aws-sdk-core (3.194.0)
aws-eventstream (~> 1, >= 1.3.0) aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8) aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.75.0) aws-sdk-kms (1.80.0)
aws-sdk-core (~> 3, >= 3.188.0) aws-sdk-core (~> 3, >= 3.193.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.142.0) aws-sdk-s3 (1.149.0)
aws-sdk-core (~> 3, >= 3.189.0) aws-sdk-core (~> 3, >= 3.194.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8) aws-sigv4 (~> 1.8)
aws-sigv4 (1.8.0) aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4) babosa (1.0.4)
base64 (0.2.0)
claide (1.1.0) claide (1.1.0)
colored (1.2) colored (1.2)
colored2 (3.1.2) colored2 (3.1.2)
@ -32,10 +35,10 @@ GEM
declarative (0.0.20) declarative (0.0.20)
digest-crc (0.6.5) digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0) rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20231109) domain_name (0.6.20240107)
dotenv (2.8.1) dotenv (2.8.1)
emoji_regex (3.2.3) emoji_regex (3.2.3)
excon (0.109.0) excon (0.110.0)
faraday (1.10.3) faraday (1.10.3)
faraday-em_http (~> 1.0) faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0) faraday-em_synchrony (~> 1.0)
@ -64,15 +67,15 @@ GEM
faraday-retry (1.0.3) faraday-retry (1.0.3)
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fastimage (2.3.0) fastimage (2.3.1)
fastlane (2.219.0) fastlane (2.220.0)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0) addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0) artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0) aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0) babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0) bundler (>= 1.12.0, < 3.0.0)
colored colored (~> 1.2)
commander (~> 4.6) commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0) dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0) emoji_regex (>= 0.1, < 4.0)
@ -93,10 +96,10 @@ GEM
mini_magick (>= 4.9.4, < 5.0.0) mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (>= 2.0.0, < 3.0.0) multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2) naturally (~> 2.2)
optparse (>= 0.1.1) optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0) plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0) rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3) security (= 0.1.5)
simctl (~> 1.6.3) simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0) terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 3) terminal-table (~> 3)
@ -105,11 +108,11 @@ GEM
word_wrap (~> 1.0.0) word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0) xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0) xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
gh_inspector (1.1.3) gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0) google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a) google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.2) google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1) addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a) httpclient (>= 2.8.1, < 3.a)
@ -117,24 +120,23 @@ GEM
representable (~> 3.0) representable (~> 3.0)
retriable (>= 2.0, < 4.a) retriable (>= 2.0, < 4.a)
rexml rexml
webrick
google-apis-iamcredentials_v1 (0.17.0) google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a) google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0) google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a) google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.29.0) google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a) google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.6.1) google-cloud-core (1.7.0)
google-cloud-env (>= 1.0, < 3.a) google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0) google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0) google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0) faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.3.1) google-cloud-errors (1.4.0)
google-cloud-storage (1.45.0) google-cloud-storage (1.47.0)
addressable (~> 2.8) addressable (~> 2.8)
digest-crc (~> 0.4) digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1) google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.29.0) google-apis-storage_v1 (~> 0.31.0)
google-cloud-core (~> 1.6) google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0) mini_mime (~> 1.0)
@ -149,30 +151,33 @@ GEM
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
jmespath (1.6.2) jmespath (1.6.2)
json (2.7.1) json (2.7.2)
jwt (2.7.1) jwt (2.8.1)
base64
mini_magick (4.12.0) mini_magick (4.12.0)
mini_mime (1.1.5) mini_mime (1.1.5)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.3.0) multipart-post (2.4.0)
nanaimo (0.3.0) nanaimo (0.3.0)
naturally (2.2.1) naturally (2.2.1)
optparse (0.4.0) nkf (0.2.0)
optparse (0.5.0)
os (1.1.4) os (1.1.4)
plist (3.7.1) plist (3.7.1)
public_suffix (5.0.4) public_suffix (5.0.5)
rake (13.1.0) rake (13.2.1)
representable (3.2.0) representable (3.2.0)
declarative (< 0.1.0) declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0) trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0) uber (< 0.2.0)
retriable (3.1.2) retriable (3.1.2)
rexml (3.2.6) rexml (3.2.8)
strscan (>= 3.0.9)
rouge (2.0.7) rouge (2.0.7)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
security (0.1.3) security (0.1.5)
signet (0.18.0) signet (0.19.0)
addressable (~> 2.8) addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a) faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
@ -180,6 +185,7 @@ GEM
simctl (1.6.10) simctl (1.6.10)
CFPropertyList CFPropertyList
naturally naturally
strscan (3.1.0)
terminal-notifier (2.0.0) terminal-notifier (2.0.0)
terminal-table (3.0.2) terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
@ -190,9 +196,8 @@ GEM
tty-cursor (~> 0.7) tty-cursor (~> 0.7)
uber (0.1.0) uber (0.1.0)
unicode-display_width (2.5.0) unicode-display_width (2.5.0)
webrick (1.8.1)
word_wrap (1.0.0) word_wrap (1.0.0)
xcodeproj (1.23.0) xcodeproj (1.24.0)
CFPropertyList (>= 2.3.3, < 4.0) CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3) atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)

@ -1,32 +1,31 @@
@file:Suppress("UnstableApiUsage") @file:Suppress("UnstableApiUsage")
import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id("com.android.application") alias(libs.plugins.android.application)
id("com.google.gms.google-services") id("com.google.gms.google-services")
id("com.google.firebase.crashlytics") id("com.google.firebase.crashlytics")
kotlin("android") kotlin("android")
id("dagger.hilt.android.plugin") id("dagger.hilt.android.plugin")
id("com.google.android.gms.oss-licenses-plugin") id("com.google.android.gms.oss-licenses-plugin")
id("kotlin-parcelize") alias(libs.plugins.kotlin.parcelize)
id("com.google.devtools.ksp") alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.kotlin.compose.compiler)
} }
repositories { kotlin {
mavenCentral() compilerOptions {
google() jvmTarget.set(JvmTarget.JVM_17)
maven { val composeReports = project.properties["composeMetrics"] ?: project.buildDir.absolutePath
url = uri("https://jitpack.io") freeCompilerArgs = listOf(
content { "-P",
includeGroup("com.github.tasks") "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=${composeReports}/compose-metrics",
includeModule("com.github.bitfireAT", "cert4android") "-P",
includeModule("com.github.bitfireAT", "dav4jvm") "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${composeReports}/compose-metrics",
includeModule("com.github.tasks.opentasks", "opentasks-provider") )
includeModule("com.github.QuadFlask", "colorpicker")
includeModule("com.github.twofortyfouram", "android-plugin-api-for-locale")
includeModule("com.github.franmontiel", "PersistentCookieJar")
}
} }
} }
@ -50,21 +49,16 @@ android {
textReport = true textReport = true
} }
compileSdk = 34 compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig { defaultConfig {
testApplicationId = "org.tasks.test" testApplicationId = "org.tasks.test"
applicationId = "org.tasks" applicationId = "org.tasks"
versionCode = 130700 versionCode = 130908
versionName = "13.7" versionName = "13.9.8"
targetSdk = 33 targetSdk = libs.versions.android.targetSdk.get().toInt()
minSdk = 24 minSdk = libs.versions.android.minSdk.get().toInt()
testInstrumentationRunner = "org.tasks.TestRunner" testInstrumentationRunner = "org.tasks.TestRunner"
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("room.incremental", "true")
}
} }
signingConfigs { signingConfigs {
@ -87,20 +81,6 @@ android {
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
} }
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
kotlinOptions {
jvmTarget = "17"
val composeReports = project.properties["composeMetrics"] ?: project.buildDir.absolutePath
freeCompilerArgs = listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=${composeReports}/compose-metrics",
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${composeReports}/compose-metrics",
)
}
flavorDimensions += listOf("store") flavorDimensions += listOf("store")
@Suppress("LocalVariableName") @Suppress("LocalVariableName")
@ -139,9 +119,21 @@ android {
dimension = "store" dimension = "store"
} }
} }
packagingOptions { packaging {
resources { resources {
excludes += setOf("META-INF/*.kotlin_module") excludes += setOf("META-INF/*.kotlin_module", "META-INF/INDEX.LIST")
}
}
testOptions {
managedDevices {
localDevices {
create("pixel2api30") {
device = "Pixel 2"
apiLevel = 30
systemImageSource = "aosp-atd"
}
}
} }
} }
@ -162,6 +154,7 @@ val genericImplementation by configurations
val googleplayImplementation by configurations val googleplayImplementation by configurations
dependencies { dependencies {
implementation(projects.data)
coreLibraryDesugaring(libs.desugar.jdk.libs) coreLibraryDesugaring(libs.desugar.jdk.libs)
implementation(libs.bitfire.dav4jvm) { implementation(libs.bitfire.dav4jvm) {
exclude(group = "junit") exclude(group = "junit")
@ -190,7 +183,7 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.lifecycle.viewmodel) implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.room) implementation(libs.androidx.room)
ksp(libs.androidx.room.compiler) implementation(libs.androidx.sqlite)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
implementation(libs.markwon) implementation(libs.markwon)
implementation(libs.markwon.editor) implementation(libs.markwon.editor)
@ -208,9 +201,9 @@ dependencies {
implementation(libs.kotlin.jdk8) implementation(libs.kotlin.jdk8)
implementation(libs.kotlin.immutable) implementation(libs.kotlin.immutable)
implementation(libs.kotlinx.serialization)
implementation(libs.okhttp) implementation(libs.okhttp)
implementation(libs.persistent.cookiejar) implementation(libs.persistent.cookiejar)
implementation(libs.gson)
implementation(libs.material) implementation(libs.material)
implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material3)
implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout)

@ -4,7 +4,7 @@ import com.natpryce.makeiteasy.MakeItEasy.with
import com.natpryce.makeiteasy.PropertyValue import com.natpryce.makeiteasy.PropertyValue
import com.todoroo.astrid.api.CaldavFilter import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -15,9 +15,9 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery.getQuery import org.tasks.data.TaskListQuery.getQuery
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase

@ -12,6 +12,9 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.* import org.tasks.data.*
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.entity.CaldavTask
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.TaskContainerMaker.PARENT import org.tasks.makers.TaskContainerMaker.PARENT

@ -4,7 +4,6 @@ import com.natpryce.makeiteasy.MakeItEasy.with
import com.natpryce.makeiteasy.PropertyValue import com.natpryce.makeiteasy.PropertyValue
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -14,14 +13,14 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskDao
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery.getQuery import org.tasks.data.TaskListQuery.getQuery
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Task
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavCalendarMaker
import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.TASK import org.tasks.makers.CaldavTaskMaker.TASK
import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.CaldavTaskMaker.newCaldavTask
@ -42,7 +41,7 @@ class GoogleTaskManualSortAdapterTest : InjectingTestCase() {
private lateinit var adapter: GoogleTaskManualSortAdapter private lateinit var adapter: GoogleTaskManualSortAdapter
private val tasks = ArrayList<TaskContainer>() private val tasks = ArrayList<TaskContainer>()
private val filter = GtasksFilter(newCaldavCalendar(with(CaldavCalendarMaker.UUID, "1234"))) private val filter = GtasksFilter(CaldavCalendar(uuid = "1234"))
private val dataSource = object : TaskAdapterDataSource { private val dataSource = object : TaskAdapterDataSource {
override fun getItem(position: Int) = tasks[position] override fun getItem(position: Int) = tasks[position]

@ -6,7 +6,7 @@ import com.natpryce.makeiteasy.MakeItEasy.with
import com.natpryce.makeiteasy.PropertyValue import com.natpryce.makeiteasy.PropertyValue
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -15,8 +15,8 @@ import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery.getQuery import org.tasks.data.TaskListQuery.getQuery
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase

@ -4,7 +4,7 @@ import com.natpryce.makeiteasy.MakeItEasy.with
import com.natpryce.makeiteasy.PropertyValue import com.natpryce.makeiteasy.PropertyValue
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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

@ -1,106 +1,255 @@
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))
verify(AlarmEntry(alarm, task, DateTime(2017, 9, 24, 19, 57).millis, TYPE_DATE_TIME))
} }
@Test @Test
fun ignoreStaleAlarm() = runBlocking { fun futureAlarmWithNoPastAlarm() = 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, 18).millis
)
)
)
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
verify() testResults(emptyList(), DateTime(2024, 5, 18, 18, 0).millis)
}
} }
@Test @Test
fun dontScheduleReminderForCompletedTask() = runBlocking { fun pastAlarmWithNoFutureAlarm() = 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)))
testResults(
listOf(
Notification(
taskId = 1L,
timestamp = DateTimeUtils2.currentTimeMillis(),
type = Alarm.TYPE_REL_END
)
),
0
)
}
}
@Test
fun pastRecurringAlarmWithFutureRecurrence() = runBlocking {
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
taskDao.insert(
Task(
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 futureSnoozeOverrideOverdue() = 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,
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
)
)
)
alarmDao.insert(whenDue(task)) testResults(
alarmDao.insert(whenOverdue(task)) emptyList(),
alarmDao.insert(Alarm(task, DateUtilities.ONE_HOUR, TYPE_RANDOM)) DateTimeUtils2.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)
val alarm = alarmDao.insert(Alarm(task, now.plusMonths(12).millis, TYPE_SNOOZE)) )
}
}
verify(AlarmEntry(alarm, task, now.plusMonths(12).millis, TYPE_SNOOZE)) @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)
} }
} }

@ -5,8 +5,7 @@
*/ */
package com.todoroo.astrid.dao package com.todoroo.astrid.dao
import com.todoroo.andlib.utility.DateUtilities import org.tasks.data.entity.Task
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskDeleter import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -15,6 +14,7 @@ import org.junit.Assert.*
import org.junit.Test import org.junit.Test
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -75,23 +75,23 @@ class TaskDaoTests : InjectingTestCase() {
// create hidden task // create hidden task
task = Task() task = Task()
task.title = "hidden" task.title = "hidden"
task.hideUntil = DateUtilities.now() + 10000 task.hideUntil = currentTimeMillis() + 10000
taskDao.createNew(task) taskDao.createNew(task)
// create task with deadlines // create task with deadlines
task = Task() task = Task()
task.title = "deadlineInFuture" task.title = "deadlineInFuture"
task.dueDate = DateUtilities.now() + 10000 task.dueDate = currentTimeMillis() + 10000
taskDao.createNew(task) taskDao.createNew(task)
task = Task() task = Task()
task.title = "deadlineInPast" task.title = "deadlineInPast"
task.dueDate = DateUtilities.now() - 10000 task.dueDate = currentTimeMillis() - 10000
taskDao.createNew(task) taskDao.createNew(task)
// create completed task // create completed task
task = Task() task = Task()
task.title = "completed" task.title = "completed"
task.completionDate = DateUtilities.now() - 10000 task.completionDate = currentTimeMillis() - 10000
taskDao.createNew(task) taskDao.createNew(task)
// check is active // check is active

@ -11,15 +11,12 @@ import org.junit.Assert.assertNull
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavAccount import org.tasks.data.dao.CaldavDao
import org.tasks.data.CaldavDao import org.tasks.data.dao.GoogleTaskListDao
import org.tasks.data.GoogleTaskListDao import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavCalendarMaker.ID
import org.tasks.makers.CaldavCalendarMaker.NAME
import org.tasks.makers.CaldavCalendarMaker.UUID
import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar
import org.tasks.makers.RemoteGtaskListMaker import org.tasks.makers.RemoteGtaskListMaker
import org.tasks.makers.RemoteGtaskListMaker.newRemoteList import org.tasks.makers.RemoteGtaskListMaker.newRemoteList
import javax.inject.Inject import javax.inject.Inject
@ -46,13 +43,14 @@ class GtasksListServiceTest : InjectingTestCase() {
newRemoteList( newRemoteList(
with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "Default"))) with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "Default")))
assertEquals( assertEquals(
newCaldavCalendar(with(ID, 1L), with(UUID, "1"), with(NAME, "Default")), CaldavCalendar(id = 1, account = "account", uuid = "1", name = "Default"),
googleTaskListDao.getById(1L)) googleTaskListDao.getById(1L)
)
} }
@Test @Test
fun testGetListByRemoteId() = runBlocking { fun testGetListByRemoteId() = runBlocking {
val list = newCaldavCalendar(with(UUID, "1")) val list = CaldavCalendar(uuid = "1")
list.id = googleTaskListDao.insertOrReplace(list) list.id = googleTaskListDao.insertOrReplace(list)
assertEquals(list, googleTaskListDao.getByRemoteId("1")) assertEquals(list, googleTaskListDao.getByRemoteId("1"))
} }
@ -64,18 +62,19 @@ class GtasksListServiceTest : InjectingTestCase() {
@Test @Test
fun testDeleteMissingList() = runBlocking { fun testDeleteMissingList() = runBlocking {
googleTaskListDao.insertOrReplace(newCaldavCalendar(with(ID, 1L), with(UUID, "1"))) googleTaskListDao.insertOrReplace(CaldavCalendar(id = 1, account = "account", uuid = "1"))
val taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2")) val taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2"))
setLists(taskList) setLists(taskList)
assertEquals( assertEquals(
listOf(newCaldavCalendar(with(ID, 2L), with(UUID, "2"), with(NAME, "Default"))), listOf(CaldavCalendar(id = 2, account = "account", uuid = "2", name = "Default")),
googleTaskListDao.getLists("account")) googleTaskListDao.getLists("account")
)
} }
@Test @Test
fun testUpdateListName() = runBlocking { fun testUpdateListName() = runBlocking {
googleTaskListDao.insertOrReplace( googleTaskListDao.insertOrReplace(
newCaldavCalendar(with(ID, 1L), with(UUID, "1"), with(NAME, "oldName")) CaldavCalendar(id = 1, uuid = "1", name = "oldName", account = "account")
) )
setLists( setLists(
newRemoteList( newRemoteList(
@ -90,10 +89,10 @@ class GtasksListServiceTest : InjectingTestCase() {
} }
private suspend fun setLists(vararg list: TaskList) { private suspend fun setLists(vararg list: TaskList) {
val account = CaldavAccount().apply { val account = CaldavAccount(
username = "account" username = "account",
uuid = "account" uuid = "account",
} )
caldavDao.insert(account) caldavDao.insert(account)
gtasksListService.updateLists(account, listOf(*list)) gtasksListService.updateLists(account, listOf(*list))
} }

@ -1,7 +1,7 @@
package com.todoroo.astrid.model package com.todoroo.astrid.model
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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
@ -10,7 +10,7 @@ import org.junit.Test
import org.tasks.SuspendFreeze.Companion.freezeClock import org.tasks.SuspendFreeze.Companion.freezeClock
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.time.DateTimeUtils import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -23,7 +23,7 @@ class TaskTest : InjectingTestCase() {
freezeClock { freezeClock {
val task = Task() val task = Task()
taskDao.createNew(task) taskDao.createNew(task)
assertEquals(DateTimeUtils.currentTimeMillis(), task.creationDate) assertEquals(currentTimeMillis(), task.creationDate)
} }
} }

@ -1,7 +1,6 @@
package com.todoroo.astrid.repeats package com.todoroo.astrid.repeats
import com.todoroo.andlib.utility.DateUtilities.now import org.tasks.data.entity.Task
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskCompleter 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
@ -9,9 +8,10 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue 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.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -35,7 +35,7 @@ class RepeatWithSubtasksTests : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )
@ -56,7 +56,7 @@ class RepeatWithSubtasksTests : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )

@ -5,14 +5,14 @@
*/ */
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.utility.TitleParser import com.todoroo.astrid.utility.TitleParser
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.TagDataDao import org.tasks.data.dao.TagDataDao
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import java.util.* import java.util.*

@ -1,10 +1,12 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.todoroo.astrid.api.PermaSql.* import com.todoroo.astrid.api.PermaSql.VALUE_EOD
import com.todoroo.astrid.data.Task import com.todoroo.astrid.api.PermaSql.VALUE_EOD_NEXT_WEEK
import com.todoroo.astrid.data.Task.Companion.DUE_DATE import com.todoroo.astrid.api.PermaSql.VALUE_EOD_TOMORROW
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL import org.tasks.data.entity.Task
import com.todoroo.astrid.data.Task.Companion.URGENCY_SPECIFIC_DAY import org.tasks.data.entity.Task.Companion.DUE_DATE
import org.tasks.data.entity.Task.Companion.HIDE_UNTIL
import org.tasks.data.entity.Task.Companion.URGENCY_SPECIFIC_DAY
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
@ -12,6 +14,7 @@ import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.tasks.R import org.tasks.R
import org.tasks.SuspendFreeze.Companion.freezeAt import org.tasks.SuspendFreeze.Companion.freezeAt
import org.tasks.data.createDueDate
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
@ -35,7 +38,7 @@ class TaskCreatorTest : InjectingTestCase() {
assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil) assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil)
assertEquals( assertEquals(
Task.createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 5).millis), createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 5).millis),
task.dueDate task.dueDate
) )
} }
@ -63,7 +66,7 @@ class TaskCreatorTest : InjectingTestCase() {
assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil) assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil)
assertEquals( assertEquals(
Task.createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 4).millis), createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 4).millis),
task.dueDate task.dueDate
) )
} }
@ -93,7 +96,7 @@ class TaskCreatorTest : InjectingTestCase() {
} }
assertEquals( assertEquals(
Task.createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 5).millis), createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 5).millis),
task.dueDate task.dueDate
) )
} }

@ -1,13 +1,13 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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.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 javax.inject.Inject import javax.inject.Inject

@ -11,20 +11,15 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV import org.tasks.data.dao.CaldavDao
import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavDao import org.tasks.data.entity.CaldavAccount.Companion.TYPE_CALDAV
import org.tasks.data.GoogleTaskDao import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.entity.CaldavCalendar
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.makers.CaldavAccountMaker
import org.tasks.makers.CaldavAccountMaker.ACCOUNT_TYPE
import org.tasks.makers.CaldavAccountMaker.newCaldavAccount
import org.tasks.makers.CaldavCalendarMaker
import org.tasks.makers.CaldavCalendarMaker.ACCOUNT
import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT
@ -48,8 +43,8 @@ class TaskMoverTest : InjectingTestCase() {
@Before @Before
fun setup() { fun setup() {
runBlocking { runBlocking {
caldavDao.insert(newCaldavCalendar(with(CaldavCalendarMaker.UUID, "1"), with(ACCOUNT, "account1"))) caldavDao.insert(CaldavCalendar(uuid = "1", account = "account1"))
caldavDao.insert(newCaldavCalendar(with(CaldavCalendarMaker.UUID, "2"), with(ACCOUNT, "account2"))) caldavDao.insert(CaldavCalendar(uuid = "2", account = "account2"))
} }
} }
@ -311,7 +306,7 @@ class TaskMoverTest : InjectingTestCase() {
} }
private suspend fun moveToGoogleTasks(list: String, vararg tasks: Long) { private suspend fun moveToGoogleTasks(list: String, vararg tasks: Long) {
taskMover.move(tasks.toList(), GtasksFilter(newCaldavCalendar(with(CaldavCalendarMaker.UUID, list)))) taskMover.move(tasks.toList(), GtasksFilter(CaldavCalendar(uuid = list)))
} }
private suspend fun moveToCaldavList(calendar: String, vararg tasks: Long) { private suspend fun moveToCaldavList(calendar: String, vararg tasks: Long) {
@ -319,6 +314,11 @@ class TaskMoverTest : InjectingTestCase() {
} }
private suspend fun setAccountType(account: String, type: Int) { private suspend fun setAccountType(account: String, type: Int) {
caldavDao.insert(newCaldavAccount(with(CaldavAccountMaker.UUID, account), with(ACCOUNT_TYPE, type))) caldavDao.insert(
CaldavAccount(
uuid = account,
accountType = type,
)
)
} }
} }

@ -5,7 +5,7 @@
*/ */
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.utility.TitleParser import com.todoroo.astrid.utility.TitleParser
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -16,7 +16,7 @@ import org.junit.Before
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test
import org.tasks.R import org.tasks.R
import org.tasks.data.TagDataDao import org.tasks.data.dao.TagDataDao
import org.tasks.date.DateTimeUtils import org.tasks.date.DateTimeUtils
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule

@ -3,7 +3,6 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.data.Task
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
@ -12,12 +11,12 @@ import org.junit.Test
import org.tasks.SuspendFreeze.Companion.freezeAt import org.tasks.SuspendFreeze.Companion.freezeAt
import org.tasks.TestUtilities.assertEquals import org.tasks.TestUtilities.assertEquals
import org.tasks.caldav.VtodoCache import org.tasks.caldav.VtodoCache
import org.tasks.data.CaldavCalendar import org.tasks.data.dao.CaldavDao
import org.tasks.data.CaldavDao import org.tasks.data.dao.TaskDao
import org.tasks.data.TaskDao import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Task
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
import org.tasks.makers.CaldavTaskMaker.TASK import org.tasks.makers.CaldavTaskMaker.TASK
@ -44,7 +43,7 @@ class Upgrade_11_3_Test : InjectingTestCase() {
@Before @Before
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
calendar = newCaldavCalendar() calendar = CaldavCalendar()
runBlocking { runBlocking {
caldavDao.insert(calendar) caldavDao.insert(calendar)
} }

@ -1,13 +1,13 @@
package com.todoroo.astrid.subtasks package com.todoroo.astrid.subtasks
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.data.TaskListMetadata import org.tasks.data.entity.TaskListMetadata
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)

@ -1,12 +1,12 @@
package com.todoroo.astrid.subtasks package com.todoroo.astrid.subtasks
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.data.TaskListMetadata import org.tasks.data.entity.TaskListMetadata
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)

@ -4,10 +4,10 @@ import androidx.test.InstrumentationRegistry
import com.todoroo.astrid.api.AstridOrderingFilter import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.tasks.data.TaskListMetadataDao import org.tasks.data.dao.TaskListMetadataDao
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import javax.inject.Inject import javax.inject.Inject

@ -1,9 +1,9 @@
package com.todoroo.astrid.sync package com.todoroo.astrid.sync
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.dao.TagDataDao
import org.tasks.data.TagData import org.tasks.data.entity.TagData
import org.tasks.data.TagDataDao import org.tasks.data.entity.Task
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import javax.inject.Inject import javax.inject.Inject
@ -21,9 +21,8 @@ open class NewSyncTestCase : InjectingTestCase() {
} }
suspend fun createTagData(): TagData { suspend fun createTagData(): TagData {
val tag = TagData() val tag = TagData(name = "new tag")
tag.name = "new tag" tagDataDao.insert(tag)
tagDataDao.createNew(tag)
return tag return tag
} }

@ -1,6 +1,6 @@
package com.todoroo.astrid.sync package com.todoroo.astrid.sync
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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

@ -1,15 +1,15 @@
package org.tasks.caldav package org.tasks.caldav
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
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.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.ETAG import org.tasks.makers.CaldavTaskMaker.ETAG
@ -25,12 +25,13 @@ class CaldavSynchronizerTest : CaldavTest() {
@Before @Before
override fun setUp() = runBlocking { override fun setUp() = runBlocking {
super.setUp() super.setUp()
account = CaldavAccount().apply { account = CaldavAccount(
uuid = UUIDHelper.newUUID() uuid = UUIDHelper.newUUID(),
username = "username" username = "username",
password = encryption.encrypt("password") password = encryption.encrypt("password"),
url = server.url("/remote.php/dav/calendars/user1/").toString() url = server.url("/remote.php/dav/calendars/user1/").toString(),
id = caldavDao.insert(this) ).let {
it.copy(id = caldavDao.insert(it))
} }
} }
@ -45,11 +46,13 @@ class CaldavSynchronizerTest : CaldavTest() {
@Test @Test
fun dontFetchCalendarIfCtagMatches() = runBlocking { fun dontFetchCalendarIfCtagMatches() = runBlocking {
caldavDao.insert(CaldavCalendar( caldavDao.insert(
account = this@CaldavSynchronizerTest.account.uuid, CaldavCalendar(
ctag = "http://sabre.io/ns/sync/1", account = this@CaldavSynchronizerTest.account.uuid,
url = "${this@CaldavSynchronizerTest.account.url}test-shared/", ctag = "http://sabre.io/ns/sync/1",
)) url = "${this@CaldavSynchronizerTest.account.url}test-shared/",
)
)
enqueue(OC_SHARE_PROPFIND) enqueue(OC_SHARE_PROPFIND)
sync() sync()

@ -9,8 +9,8 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.rules.Timeout import org.junit.rules.Timeout
import org.tasks.R import org.tasks.R
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption import org.tasks.security.KeyStoreEncryption

@ -1,19 +1,19 @@
package org.tasks.caldav package org.tasks.caldav
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
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.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavAccount.Companion.SERVER_OPEN_XCHANGE import org.tasks.data.entity.CaldavAccount.Companion.SERVER_OPEN_XCHANGE
import org.tasks.data.CaldavAccount.Companion.SERVER_OWNCLOUD import org.tasks.data.entity.CaldavAccount.Companion.SERVER_OWNCLOUD
import org.tasks.data.CaldavAccount.Companion.SERVER_SABREDAV import org.tasks.data.entity.CaldavAccount.Companion.SERVER_SABREDAV
import org.tasks.data.CaldavAccount.Companion.SERVER_TASKS import org.tasks.data.entity.CaldavAccount.Companion.SERVER_TASKS
import org.tasks.data.CaldavAccount.Companion.SERVER_UNKNOWN import org.tasks.data.entity.CaldavAccount.Companion.SERVER_UNKNOWN
import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV import org.tasks.data.entity.CaldavAccount.Companion.TYPE_CALDAV
import org.tasks.data.CaldavAccount.Companion.TYPE_TASKS import org.tasks.data.entity.CaldavAccount.Companion.TYPE_TASKS
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -79,13 +79,14 @@ class ServerDetectionTest : CaldavTest() {
vararg headers: Pair<String, String>, vararg headers: Pair<String, String>,
accountType: Int = TYPE_CALDAV accountType: Int = TYPE_CALDAV
) { ) {
account = CaldavAccount().apply { account = CaldavAccount(
uuid = UUIDHelper.newUUID() uuid = UUIDHelper.newUUID(),
username = "username" username = "username",
password = encryption.encrypt("password") password = encryption.encrypt("password"),
url = server.url("/remote.php/dav/calendars/user1/").toString() url = server.url("/remote.php/dav/calendars/user1/").toString(),
id = caldavDao.insert(this) accountType = accountType,
this.accountType = accountType ).let {
it.copy(id = caldavDao.insert(it))
} }
this.headers.putAll(headers) this.headers.putAll(headers)
enqueue(NO_CALENDARS) enqueue(NO_CALENDARS)

@ -1,16 +1,16 @@
package org.tasks.caldav package org.tasks.caldav
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
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.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_WRITE import org.tasks.data.entity.CaldavCalendar.Companion.ACCESS_READ_WRITE
import org.tasks.data.PrincipalDao import org.tasks.data.dao.PrincipalDao
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import javax.inject.Inject import javax.inject.Inject
@ -22,12 +22,13 @@ class SharingMailboxDotOrgTest : CaldavTest() {
@Test @Test
fun ownerAccess() = runBlocking { fun ownerAccess() = runBlocking {
account = CaldavAccount().apply { account = CaldavAccount(
uuid = UUIDHelper.newUUID() uuid = UUIDHelper.newUUID(),
username = "3" username = "3",
password = encryption.encrypt("password") password = encryption.encrypt("password"),
url = server.url("/caldav/").toString() url = server.url("/caldav/").toString(),
id = caldavDao.insert(this) ).let {
it.copy(id = caldavDao.insert(it))
} }
val calendar = CaldavCalendar( val calendar = CaldavCalendar(
account = this@SharingMailboxDotOrgTest.account.uuid, account = this@SharingMailboxDotOrgTest.account.uuid,
@ -45,12 +46,13 @@ class SharingMailboxDotOrgTest : CaldavTest() {
@Test @Test
fun principalForSharee() = runBlocking { fun principalForSharee() = runBlocking {
account = CaldavAccount().apply { account = CaldavAccount(
uuid = UUIDHelper.newUUID() uuid = UUIDHelper.newUUID(),
username = "3" username = "3",
password = encryption.encrypt("password") password = encryption.encrypt("password"),
url = server.url("/caldav/").toString() url = server.url("/caldav/").toString(),
id = caldavDao.insert(this) ).let {
it.copy(id = caldavDao.insert(it))
} }
val calendar = CaldavCalendar( val calendar = CaldavCalendar(
account = this@SharingMailboxDotOrgTest.account.uuid, account = this@SharingMailboxDotOrgTest.account.uuid,

@ -1,16 +1,16 @@
package org.tasks.caldav package org.tasks.caldav
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
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.* import org.junit.Assert.*
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER import org.tasks.data.entity.CaldavCalendar.Companion.ACCESS_OWNER
import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_ONLY import org.tasks.data.entity.CaldavCalendar.Companion.ACCESS_READ_ONLY
import org.tasks.data.PrincipalDao import org.tasks.data.dao.PrincipalDao
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import javax.inject.Inject import javax.inject.Inject
@ -21,12 +21,13 @@ class SharingOwncloudTest : CaldavTest() {
@Inject lateinit var principalDao: PrincipalDao @Inject lateinit var principalDao: PrincipalDao
private suspend fun setupAccount(user: String) { private suspend fun setupAccount(user: String) {
account = CaldavAccount().apply { account = CaldavAccount(
uuid = UUIDHelper.newUUID() uuid = UUIDHelper.newUUID(),
username = user username = user,
password = encryption.encrypt("password") password = encryption.encrypt("password"),
url = server.url("/remote.php/dav/calendars/$user/").toString() url = server.url("/remote.php/dav/calendars/$user/").toString(),
id = caldavDao.insert(this) ).let {
it.copy(id = caldavDao.insert(it))
} }
} }

@ -1,18 +1,18 @@
package org.tasks.caldav package org.tasks.caldav
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
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.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER import org.tasks.data.entity.CaldavCalendar.Companion.ACCESS_OWNER
import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_WRITE import org.tasks.data.entity.CaldavCalendar.Companion.ACCESS_READ_WRITE
import org.tasks.data.CaldavCalendar.Companion.INVITE_ACCEPTED import org.tasks.data.entity.CaldavCalendar.Companion.INVITE_ACCEPTED
import org.tasks.data.PrincipalDao import org.tasks.data.dao.PrincipalDao
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import javax.inject.Inject import javax.inject.Inject
@ -23,12 +23,13 @@ class SharingSabredavTest : CaldavTest() {
@Inject lateinit var principalDao: PrincipalDao @Inject lateinit var principalDao: PrincipalDao
private suspend fun setupAccount(user: String) { private suspend fun setupAccount(user: String) {
account = CaldavAccount().apply { account = CaldavAccount(
uuid = UUIDHelper.newUUID() uuid = UUIDHelper.newUUID(),
username = user username = user,
password = encryption.encrypt("password") password = encryption.encrypt("password"),
url = server.url("/calendars/$user/").toString() url = server.url("/calendars/$user/").toString(),
id = caldavDao.insert(this) ).let {
it.copy(id = caldavDao.insert(it))
} }
} }

@ -2,7 +2,6 @@ package org.tasks.data
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.natpryce.makeiteasy.PropertyValue import com.natpryce.makeiteasy.PropertyValue
import com.todoroo.andlib.utility.DateUtilities.now
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
@ -10,11 +9,15 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Test import org.junit.Test
import org.tasks.SuspendFreeze.Companion.freezeAt import org.tasks.SuspendFreeze.Companion.freezeAt
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.TaskDao
import org.tasks.data.entity.CaldavTask
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.TaskContainerMaker import org.tasks.makers.TaskContainerMaker
import org.tasks.makers.TaskContainerMaker.CREATED import org.tasks.makers.TaskContainerMaker.CREATED
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -101,7 +104,9 @@ class CaldavDaoShiftTests : InjectingTestCase() {
fun ignoreMovedTasksWhenShiftingDown() = runBlocking { fun ignoreMovedTasksWhenShiftingDown() = runBlocking {
val created = DateTime(2020, 5, 17, 9, 53, 17) val created = DateTime(2020, 5, 17, 9, 53, 17)
addTask(with(CREATED, created)) addTask(with(CREATED, created))
caldavDao.update(caldavDao.getTask(tasks[0].id).apply { this?.deleted = now() }!!) caldavDao.update(caldavDao.getTask(tasks[0].id).apply { this?.deleted =
currentTimeMillis()
}!!)
caldavDao.shiftDown("calendar", 0, created.toAppleEpoch()) caldavDao.shiftDown("calendar", 0, created.toAppleEpoch())
@ -112,7 +117,7 @@ class CaldavDaoShiftTests : InjectingTestCase() {
fun ignoreDeletedTasksWhenShiftingDown() = runBlocking { fun ignoreDeletedTasksWhenShiftingDown() = runBlocking {
val created = DateTime(2020, 5, 17, 9, 53, 17) val created = DateTime(2020, 5, 17, 9, 53, 17)
addTask(with(CREATED, created)) addTask(with(CREATED, created))
taskDao.update(taskDao.fetch(tasks[0].id).apply { this?.deletionDate = now() }!!) taskDao.update(taskDao.fetch(tasks[0].id).apply { this?.deletionDate = currentTimeMillis() }!!)
caldavDao.shiftDown("calendar", 0, created.toAppleEpoch()) caldavDao.shiftDown("calendar", 0, created.toAppleEpoch())

@ -2,12 +2,18 @@ package org.tasks.data
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.helper.UUIDHelper
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.* import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.TagDao
import org.tasks.data.dao.TagDataDao
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavTask
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.CREATION_TIME import org.tasks.makers.TaskMaker.CREATION_TIME
@ -85,8 +91,7 @@ class CaldavDaoTests : InjectingTestCase() {
@Test @Test
fun noResultsForEmptyAccounts() = runBlocking { fun noResultsForEmptyAccounts() = runBlocking {
val caldavAccount = CaldavAccount() val caldavAccount = CaldavAccount(uuid = UUIDHelper.newUUID())
caldavAccount.uuid = UUIDHelper.newUUID()
caldavDao.insert(caldavAccount) caldavDao.insert(caldavAccount)
assertTrue(caldavDao.getCaldavFilters(caldavAccount.uuid!!).isEmpty()) assertTrue(caldavDao.getCaldavFilters(caldavAccount.uuid!!).isEmpty())
} }

@ -2,13 +2,18 @@ package org.tasks.data
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.helper.UUIDHelper
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.* import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavDao.Companion.LOCAL import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.CaldavDao.Companion.LOCAL
import org.tasks.data.dao.DeletionDao
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.CaldavTask
import org.tasks.date.DateTimeUtils.newDateTime 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
@ -16,7 +21,7 @@ import org.tasks.makers.TaskMaker.CREATION_TIME
import org.tasks.makers.TaskMaker.DELETION_TIME import org.tasks.makers.TaskMaker.DELETION_TIME
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -43,7 +48,7 @@ class DeletionDaoTests : InjectingTestCase() {
deletionDao.markDeleted(listOf(task.id)) deletionDao.markDeleted(listOf(task.id))
task = taskDao.fetch(task.id)!! task = taskDao.fetch(task.id)!!
assertTrue(task.modificationDate > task.creationDate) assertTrue(task.modificationDate > task.creationDate)
assertTrue(task.modificationDate < DateTimeUtils.currentTimeMillis()) assertTrue(task.modificationDate < currentTimeMillis())
} }
@Test @Test
@ -53,7 +58,7 @@ class DeletionDaoTests : InjectingTestCase() {
deletionDao.markDeleted(listOf(task.id)) deletionDao.markDeleted(listOf(task.id))
task = taskDao.fetch(task.id)!! task = taskDao.fetch(task.id)!!
assertTrue(task.deletionDate > task.creationDate) assertTrue(task.deletionDate > task.creationDate)
assertTrue(task.deletionDate < DateTimeUtils.currentTimeMillis()) assertTrue(task.deletionDate < currentTimeMillis())
} }
@Test @Test

@ -9,12 +9,15 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.dao.GoogleTaskListDao
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.CaldavTask
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavAccountMaker.ACCOUNT_TYPE
import org.tasks.makers.CaldavAccountMaker.newCaldavAccount
import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT
@ -35,8 +38,8 @@ class GoogleTaskDaoTests : InjectingTestCase() {
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
runBlocking { runBlocking {
caldavDao.insert(newCaldavAccount(with(ACCOUNT_TYPE, TYPE_GOOGLE_TASKS))) caldavDao.insert(CaldavAccount(uuid = "account", accountType = TYPE_GOOGLE_TASKS))
caldavDao.insert(newCaldavCalendar()) caldavDao.insert(CaldavCalendar(account = "account", uuid = "calendar"))
} }
} }

@ -3,8 +3,11 @@ package org.tasks.data
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.* import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.GoogleTaskListDao
import org.tasks.data.entity.CaldavAccount
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import javax.inject.Inject import javax.inject.Inject
@ -17,10 +20,10 @@ class GoogleTaskListDaoTest : InjectingTestCase() {
@Test @Test
fun noResultsForEmptyAccount() = runBlocking { fun noResultsForEmptyAccount() = runBlocking {
val account = CaldavAccount().apply { val account = CaldavAccount(
uuid = "user@gmail.com" uuid = "user@gmail.com",
username = "user@gmail.com" username = "user@gmail.com",
} )
caldavDao.insert(account) caldavDao.insert(account)
assertTrue(googleTaskListDao.getGoogleTaskFilters(account.username!!).isEmpty()) assertTrue(googleTaskListDao.getGoogleTaskFilters(account.username!!).isEmpty())

@ -1,17 +1,24 @@
package org.tasks.data package org.tasks.data
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
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.* import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.SuspendFreeze.Companion.freezeAt import org.tasks.SuspendFreeze.Companion.freezeAt
import org.tasks.caldav.GeoUtils.toLikeString import org.tasks.caldav.GeoUtils.toLikeString
import org.tasks.data.Alarm.Companion.TYPE_SNOOZE import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.LocationDao
import org.tasks.data.entity.Alarm
import org.tasks.data.entity.Alarm.Companion.TYPE_SNOOZE
import org.tasks.data.entity.Geofence
import org.tasks.data.entity.Place
import org.tasks.data.entity.Task
import org.tasks.date.DateTimeUtils.newDateTime 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
@ -21,6 +28,7 @@ import org.tasks.makers.TaskMaker.DUE_TIME
import org.tasks.makers.TaskMaker.HIDE_TYPE import org.tasks.makers.TaskMaker.HIDE_TYPE
import org.tasks.makers.TaskMaker.ID import org.tasks.makers.TaskMaker.ID
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -113,61 +121,89 @@ class LocationDaoTest : InjectingTestCase() {
@Test @Test
fun ignoreArrivalForSnoozedTask() = runBlocking { fun ignoreArrivalForSnoozedTask() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
val task = taskDao.createNew(newTask()) val task = taskDao.createNew(newTask())
alarmDao.insert(Alarm(task, newDateTime().plusMinutes(15).millis, TYPE_SNOOZE)) alarmDao.insert(
Alarm(
task = task,
time = newDateTime().plusMinutes(15).millis,
type = TYPE_SNOOZE
)
)
locationDao.insert(Geofence(task = task, place = place.uid, isArrival = true)) locationDao.insert(Geofence(task = task, place = place.uid, isArrival = true))
assertTrue(locationDao.getArrivalGeofences(place.uid!!, now()).isEmpty()) assertTrue(locationDao.getArrivalGeofences(place.uid!!, currentTimeMillis()).isEmpty())
} }
} }
@Test @Test
fun ignoreDepartureForSnoozedTask() = runBlocking { fun ignoreDepartureForSnoozedTask() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
val task = taskDao.createNew(newTask()) val task = taskDao.createNew(newTask())
alarmDao.insert(Alarm(task, newDateTime().plusMinutes(15).millis, TYPE_SNOOZE)) alarmDao.insert(
Alarm(
task = task,
time = newDateTime().plusMinutes(15).millis,
type = TYPE_SNOOZE
)
)
locationDao.insert(Geofence(task = task, place = place.uid, isDeparture = true)) locationDao.insert(Geofence(task = task, place = place.uid, isDeparture = true))
assertTrue(locationDao.getDepartureGeofences(place.uid!!, now()).isEmpty()) assertTrue(locationDao.getDepartureGeofences(place.uid!!, currentTimeMillis()).isEmpty())
} }
} }
@Test @Test
fun getArrivalWithElapsedSnooze() = runBlocking { fun getArrivalWithElapsedSnooze() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
val task = taskDao.createNew(newTask()) val task = taskDao.createNew(newTask())
alarmDao.insert(Alarm(task, newDateTime().minusMinutes(15).millis, TYPE_SNOOZE)) alarmDao.insert(
Alarm(
task = task,
time = newDateTime().minusMinutes(15).millis,
type = TYPE_SNOOZE
)
)
val geofence = Geofence(task = task, place = place.uid, isArrival = true) val geofence = Geofence(task = task, place = place.uid, isArrival = true)
.let { it.copy(id = locationDao.insert(it)) } .let { it.copy(id = locationDao.insert(it)) }
assertEquals(listOf(geofence), locationDao.getArrivalGeofences(place.uid!!, now())) assertEquals(listOf(geofence), locationDao.getArrivalGeofences(place.uid!!,
currentTimeMillis()
))
} }
} }
@Test @Test
fun getDepartureWithElapsedSnooze() = runBlocking { fun getDepartureWithElapsedSnooze() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
val task = taskDao.createNew(newTask()) val task = taskDao.createNew(newTask())
alarmDao.insert(Alarm(task, newDateTime().minusMinutes(15).millis, TYPE_SNOOZE)) alarmDao.insert(
Alarm(
task = task,
time = newDateTime().minusMinutes(15).millis,
type = TYPE_SNOOZE
)
)
val geofence = Geofence(task = task, place = place.uid, isDeparture = true) val geofence = Geofence(task = task, place = place.uid, isDeparture = true)
.let { it.copy(id = locationDao.insert(it)) } .let { it.copy(id = locationDao.insert(it)) }
assertEquals(listOf(geofence), locationDao.getDepartureGeofences(place.uid!!, now())) assertEquals(listOf(geofence), locationDao.getDepartureGeofences(place.uid!!,
currentTimeMillis()
))
} }
} }
@Test @Test
fun ignoreArrivalForHiddenTask() = runBlocking { fun ignoreArrivalForHiddenTask() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
taskDao.createNew(newTask( taskDao.createNew(newTask(
@ -176,13 +212,13 @@ class LocationDaoTest : InjectingTestCase() {
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE_TIME))) with(HIDE_TYPE, Task.HIDE_UNTIL_DUE_TIME)))
locationDao.insert(Geofence(task = 1, place = place.uid, isArrival = true)) locationDao.insert(Geofence(task = 1, place = place.uid, isArrival = true))
assertTrue(locationDao.getArrivalGeofences(place.uid!!, now()).isEmpty()) assertTrue(locationDao.getArrivalGeofences(place.uid!!, currentTimeMillis()).isEmpty())
} }
} }
@Test @Test
fun ignoreDepartureForHiddenTask() = runBlocking { fun ignoreDepartureForHiddenTask() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
taskDao.createNew(newTask( taskDao.createNew(newTask(
@ -191,13 +227,13 @@ class LocationDaoTest : InjectingTestCase() {
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE_TIME))) with(HIDE_TYPE, Task.HIDE_UNTIL_DUE_TIME)))
locationDao.insert(Geofence(task = 1, place = place.uid, isDeparture = true)) locationDao.insert(Geofence(task = 1, place = place.uid, isDeparture = true))
assertTrue(locationDao.getDepartureGeofences(place.uid!!, now()).isEmpty()) assertTrue(locationDao.getDepartureGeofences(place.uid!!, currentTimeMillis()).isEmpty())
} }
} }
@Test @Test
fun getArrivalWithElapsedHideUntil() = runBlocking { fun getArrivalWithElapsedHideUntil() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
taskDao.createNew(newTask( taskDao.createNew(newTask(
@ -209,13 +245,15 @@ class LocationDaoTest : InjectingTestCase() {
it.copy(id = locationDao.insert(it)) it.copy(id = locationDao.insert(it))
} }
assertEquals(listOf(geofence), locationDao.getArrivalGeofences(place.uid!!, now())) assertEquals(listOf(geofence), locationDao.getArrivalGeofences(place.uid!!,
currentTimeMillis()
))
} }
} }
@Test @Test
fun getDepartureWithElapsedHideUntil() = runBlocking { fun getDepartureWithElapsedHideUntil() = runBlocking {
freezeAt(now()).thawAfter { freezeAt(currentTimeMillis()).thawAfter {
val place = Place() val place = Place()
locationDao.insert(place) locationDao.insert(place)
taskDao.createNew(newTask( taskDao.createNew(newTask(
@ -225,7 +263,9 @@ class LocationDaoTest : InjectingTestCase() {
val geofence = Geofence(task = 1, place = place.uid, isDeparture = true) val geofence = Geofence(task = 1, place = place.uid, isDeparture = true)
.let { it.copy(id = locationDao.insert(it)) } .let { it.copy(id = locationDao.insert(it)) }
assertEquals(listOf(geofence), locationDao.getDepartureGeofences(place.uid!!, now())) assertEquals(listOf(geofence), locationDao.getDepartureGeofences(place.uid!!,
currentTimeMillis()
))
} }
} }
} }

@ -3,7 +3,6 @@ package org.tasks.data
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.helper.UUIDHelper
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
@ -11,11 +10,12 @@ import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.R import org.tasks.R
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavAccountMaker.newCaldavAccount
import org.tasks.makers.CaldavCalendarMaker.UUID
import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.TASK import org.tasks.makers.CaldavTaskMaker.TASK
import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.CaldavTaskMaker.newCaldavTask
@ -40,9 +40,9 @@ class ManualGoogleTaskQueryTest : InjectingTestCase() {
super.setUp() super.setUp()
preferences.clear() preferences.clear()
preferences.setBoolean(R.string.p_manual_sort, true) preferences.setBoolean(R.string.p_manual_sort, true)
val calendar = newCaldavCalendar(with(UUID, "1234")) val calendar = CaldavCalendar(uuid = "1234")
runBlocking { runBlocking {
caldavDao.insert(newCaldavAccount()) caldavDao.insert(CaldavAccount())
caldavDao.insert(calendar) caldavDao.insert(calendar)
} }
filter = GtasksFilter(calendar) filter = GtasksFilter(calendar)

@ -8,14 +8,12 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.dao.TagDao
import org.tasks.data.dao.TagDataDao
import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.TagDataMaker.NAME
import org.tasks.makers.TagDataMaker.newTagData
import org.tasks.makers.TagMaker.TAGDATA
import org.tasks.makers.TagMaker.TAGUID
import org.tasks.makers.TagMaker.TASK
import org.tasks.makers.TagMaker.newTag
import org.tasks.makers.TaskMaker.ID import org.tasks.makers.TaskMaker.ID
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import javax.inject.Inject import javax.inject.Inject
@ -29,13 +27,13 @@ class TagDataDaoTest : InjectingTestCase() {
@Test @Test
fun tagDataOrderedByNameIgnoresNullNames() = runBlocking { fun tagDataOrderedByNameIgnoresNullNames() = runBlocking {
tagDataDao.createNew(newTagData(with(NAME, null as String?))) tagDataDao.insert(TagData(name = null))
assertTrue(tagDataDao.tagDataOrderedByName().isEmpty()) assertTrue(tagDataDao.tagDataOrderedByName().isEmpty())
} }
@Test @Test
fun tagDataOrderedByNameIgnoresEmptyNames() = runBlocking { fun tagDataOrderedByNameIgnoresEmptyNames() = runBlocking {
tagDataDao.createNew(newTagData(with(NAME, ""))) tagDataDao.insert(TagData(name = ""))
assertTrue(tagDataDao.tagDataOrderedByName().isEmpty()) assertTrue(tagDataDao.tagDataOrderedByName().isEmpty())
} }
@ -46,20 +44,19 @@ class TagDataDaoTest : InjectingTestCase() {
@Test @Test
fun getTagWithCaseFixesCase() = runBlocking { fun getTagWithCaseFixesCase() = runBlocking {
tagDataDao.createNew(newTagData(with(NAME, "Derp"))) tagDataDao.insert(TagData(name = "Derp"))
assertEquals("Derp", tagDataDao.getTagWithCase("derp")) assertEquals("Derp", tagDataDao.getTagWithCase("derp"))
} }
@Test @Test
fun getTagsByName() = runBlocking { fun getTagsByName() = runBlocking {
val tagData = newTagData(with(NAME, "Derp")) val tagData = TagData(name = "Derp").let { it.copy(id = tagDataDao.insert(it)) }
tagDataDao.createNew(tagData)
assertEquals(listOf(tagData), tagDataDao.getTags(listOf("Derp"))) assertEquals(listOf(tagData), tagDataDao.getTags(listOf("Derp")))
} }
@Test @Test
fun getTagsByNameCaseSensitive() = runBlocking { fun getTagsByNameCaseSensitive() = runBlocking {
tagDataDao.createNew(newTagData(with(NAME, "Derp"))) tagDataDao.insert(TagData(name = "Derp"))
assertTrue(tagDataDao.getTags(listOf("derp")).isEmpty()) assertTrue(tagDataDao.getTags(listOf("derp")).isEmpty())
} }
@ -69,20 +66,18 @@ class TagDataDaoTest : InjectingTestCase() {
val taskTwo = newTask() val taskTwo = newTask()
taskDao.createNew(taskOne) taskDao.createNew(taskOne)
taskDao.createNew(taskTwo) taskDao.createNew(taskTwo)
val tagOne = newTagData(with(NAME, "one")) val tagOne = TagData(name = "one").let { it.copy(id = tagDataDao.insert(it)) }
val tagTwo = newTagData(with(NAME, "two")) val tagTwo = TagData(name = "two").let { it.copy(id = tagDataDao.insert(it)) }
tagDataDao.createNew(tagOne) tagDao.insert(Tag(task = taskOne.id, taskUid = taskOne.uuid, tagUid = tagOne.remoteId))
tagDataDao.createNew(tagTwo) tagDao.insert(Tag(task = taskTwo.id, taskUid = taskTwo.uuid, tagUid = tagTwo.remoteId))
tagDao.insert(newTag(with(TAGDATA, tagOne), with(TASK, taskOne)))
tagDao.insert(newTag(with(TAGDATA, tagTwo), with(TASK, taskTwo)))
assertEquals(listOf(tagOne), tagDataDao.getTagDataForTask(taskOne.id)) assertEquals(listOf(tagOne), tagDataDao.getTagDataForTask(taskOne.id))
} }
@Test @Test
fun getEmptyTagSelections() = runBlocking { fun getEmptyTagSelections() = runBlocking {
val selections = tagDataDao.getTagSelections(listOf(1L)) val selections = tagDataDao.getTagSelections(listOf(1L))
assertTrue(selections.first!!.isEmpty()) assertTrue(selections.first.isEmpty())
assertTrue(selections.second!!.isEmpty()) assertTrue(selections.second.isEmpty())
} }
@Test @Test
@ -97,7 +92,7 @@ class TagDataDaoTest : InjectingTestCase() {
fun getEmptyPartialSelections() = runBlocking { fun getEmptyPartialSelections() = runBlocking {
newTag(1, "tag1") newTag(1, "tag1")
newTag(2, "tag1") newTag(2, "tag1")
assertTrue(tagDataDao.getTagSelections(listOf(1L, 2L)).first!!.isEmpty()) assertTrue(tagDataDao.getTagSelections(listOf(1L, 2L)).first.isEmpty())
} }
@Test @Test
@ -111,15 +106,15 @@ class TagDataDaoTest : InjectingTestCase() {
fun getEmptyCommonSelections() = runBlocking { fun getEmptyCommonSelections() = runBlocking {
newTag(1, "tag1") newTag(1, "tag1")
newTag(2, "tag2") newTag(2, "tag2")
assertTrue(tagDataDao.getTagSelections(listOf(1L, 2L)).second!!.isEmpty()) assertTrue(tagDataDao.getTagSelections(listOf(1L, 2L)).second.isEmpty())
} }
@Test @Test
fun getSelectionsWithNoTags() = runBlocking { fun getSelectionsWithNoTags() = runBlocking {
newTag(1) newTag(1)
val selections = tagDataDao.getTagSelections(listOf(1L)) val selections = tagDataDao.getTagSelections(listOf(1L))
assertTrue(selections.first!!.isEmpty()) assertTrue(selections.first.isEmpty())
assertTrue(selections.second!!.isEmpty()) assertTrue(selections.second.isEmpty())
} }
@Test @Test
@ -128,14 +123,14 @@ class TagDataDaoTest : InjectingTestCase() {
newTag(2) newTag(2)
val selections = tagDataDao.getTagSelections(listOf(1L, 2L)) val selections = tagDataDao.getTagSelections(listOf(1L, 2L))
assertEquals(setOf("tag1"), selections.first) assertEquals(setOf("tag1"), selections.first)
assertTrue(selections.second!!.isEmpty()) assertTrue(selections.second.isEmpty())
} }
private suspend fun newTag(taskId: Long, vararg tags: String) { private suspend fun newTag(taskId: Long, vararg tags: String) {
val task = newTask(with(ID, taskId)) val task = newTask(with(ID, taskId))
taskDao.createNew(task) taskDao.createNew(task)
for (tag in tags) { for (tag in tags) {
tagDao.insert(newTag(with(TASK, task), with(TAGUID, tag))) tagDao.insert(Tag(task = task.id, taskUid = task.uuid, tagUid = tag))
} }
} }
} }

@ -6,8 +6,7 @@
package org.tasks.data package org.tasks.data
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.andlib.utility.DateUtilities import org.tasks.data.entity.Task
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskDeleter import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -15,10 +14,12 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Test import org.junit.Test
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.PARENT import org.tasks.makers.TaskMaker.PARENT
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -44,23 +45,23 @@ class TaskDaoTests : InjectingTestCase() {
// create hidden task // create hidden task
task = Task() task = Task()
task.title = "hidden" task.title = "hidden"
task.hideUntil = DateUtilities.now() + 10000 task.hideUntil = currentTimeMillis() + 10000
taskDao.createNew(task) taskDao.createNew(task)
// create task with deadlines // create task with deadlines
task = Task() task = Task()
task.title = "deadlineInFuture" task.title = "deadlineInFuture"
task.dueDate = DateUtilities.now() + 10000 task.dueDate = currentTimeMillis() + 10000
taskDao.createNew(task) taskDao.createNew(task)
task = Task() task = Task()
task.title = "deadlineInPast" task.title = "deadlineInPast"
task.dueDate = DateUtilities.now() - 10000 task.dueDate = currentTimeMillis() - 10000
taskDao.createNew(task) taskDao.createNew(task)
// create completed task // create completed task
task = Task() task = Task()
task.title = "completed" task.title = "completed"
task.completionDate = DateUtilities.now() - 10000 task.completionDate = currentTimeMillis() - 10000
taskDao.createNew(task) taskDao.createNew(task)
// check is active // check is active

@ -8,10 +8,15 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.TagDao
import org.tasks.data.dao.TagDataDao
import org.tasks.data.dao.UpgraderDao
import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.TagDataMaker
import org.tasks.makers.TagMaker
import org.tasks.makers.TaskMaker import org.tasks.makers.TaskMaker
import javax.inject.Inject import javax.inject.Inject
@ -29,12 +34,12 @@ class UpgraderDaoTests : InjectingTestCase() {
fun getCaldavTasksWithTags() = runBlocking { fun getCaldavTasksWithTags() = runBlocking {
val task = TaskMaker.newTask(MakeItEasy.with(TaskMaker.ID, 1L)) val task = TaskMaker.newTask(MakeItEasy.with(TaskMaker.ID, 1L))
taskDao.createNew(task) taskDao.createNew(task)
val one = TagDataMaker.newTagData() val one = TagData()
val two = TagDataMaker.newTagData() val two = TagData()
tagDataDao.createNew(one) tagDataDao.insert(one)
tagDataDao.createNew(two) tagDataDao.insert(two)
tagDao.insert(TagMaker.newTag(MakeItEasy.with(TagMaker.TASK, task), MakeItEasy.with(TagMaker.TAGDATA, one))) tagDao.insert(Tag(task = task.id, taskUid = task.uuid, tagUid = one.remoteId))
tagDao.insert(TagMaker.newTag(MakeItEasy.with(TagMaker.TASK, task), MakeItEasy.with(TagMaker.TAGDATA, two))) tagDao.insert(Tag(task = task.id, taskUid = task.uuid, tagUid = two.remoteId))
caldavDao.insert(CaldavTask(task = task.id, calendar = "calendar")) caldavDao.insert(CaldavTask(task = task.id, calendar = "calendar"))
assertEquals(listOf(task.id), upgraderDao.tasksWithTags()) assertEquals(listOf(task.id), upgraderDao.tasksWithTags())
} }
@ -43,9 +48,9 @@ class UpgraderDaoTests : InjectingTestCase() {
fun ignoreNonCaldavTaskWithTags() = runBlocking { fun ignoreNonCaldavTaskWithTags() = runBlocking {
val task = TaskMaker.newTask(MakeItEasy.with(TaskMaker.ID, 1L)) val task = TaskMaker.newTask(MakeItEasy.with(TaskMaker.ID, 1L))
taskDao.createNew(task) taskDao.createNew(task)
val tag = TagDataMaker.newTagData() val tag = TagData()
tagDataDao.createNew(tag) tagDataDao.insert(tag)
tagDao.insert(TagMaker.newTag(MakeItEasy.with(TagMaker.TASK, task), MakeItEasy.with(TagMaker.TAGDATA, tag))) tagDao.insert(Tag(task = task.id, taskUid = task.uuid, tagUid = tag.remoteId))
assertTrue(upgraderDao.tasksWithTags().isEmpty()) assertTrue(upgraderDao.tasksWithTags().isEmpty())
} }
@ -53,7 +58,7 @@ class UpgraderDaoTests : InjectingTestCase() {
fun ignoreCaldavTaskWithoutTags() = runBlocking { fun ignoreCaldavTaskWithoutTags() = runBlocking {
val task = TaskMaker.newTask(MakeItEasy.with(TaskMaker.ID, 1L)) val task = TaskMaker.newTask(MakeItEasy.with(TaskMaker.ID, 1L))
taskDao.createNew(task) taskDao.createNew(task)
tagDataDao.createNew(TagDataMaker.newTagData()) tagDataDao.insert(TagData())
caldavDao.insert(CaldavTask(task = task.id, calendar = "calendar")) caldavDao.insert(CaldavTask(task = task.id, calendar = "calendar"))
assertTrue(upgraderDao.tasksWithTags().isEmpty()) assertTrue(upgraderDao.tasksWithTags().isEmpty())
} }

@ -2,7 +2,7 @@ package org.tasks.gtasks
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Test import org.junit.Test

@ -2,7 +2,7 @@ package org.tasks.injection
import android.content.Context import android.content.Context
import androidx.room.Room import androidx.room.Room
import com.todoroo.astrid.dao.Database import org.tasks.data.db.Database
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

@ -8,7 +8,7 @@ package org.tasks.jobs
import android.net.Uri import android.net.Uri
import androidx.test.InstrumentationRegistry import androidx.test.InstrumentationRegistry
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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

@ -1,7 +1,7 @@
package org.tasks.opentasks package org.tasks.opentasks
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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

@ -1,11 +1,13 @@
package org.tasks.opentasks package org.tasks.opentasks
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.data.Task
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.* import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.SuspendFreeze.Companion.freezeAt import org.tasks.SuspendFreeze.Companion.freezeAt
import org.tasks.TestUtilities.withTZ import org.tasks.TestUtilities.withTZ
@ -13,27 +15,25 @@ import org.tasks.caldav.iCalendar.Companion.collapsed
import org.tasks.caldav.iCalendar.Companion.order import org.tasks.caldav.iCalendar.Companion.order
import org.tasks.caldav.iCalendar.Companion.parent import org.tasks.caldav.iCalendar.Companion.parent
import org.tasks.caldav.iCalendar.Companion.snooze import org.tasks.caldav.iCalendar.Companion.snooze
import org.tasks.data.Alarm import org.tasks.data.dao.AlarmDao
import org.tasks.data.Alarm.Companion.TYPE_SNOOZE import org.tasks.data.dao.TagDao
import org.tasks.data.AlarmDao import org.tasks.data.dao.TagDataDao
import org.tasks.data.TagDao import org.tasks.data.entity.Alarm
import org.tasks.data.TagDataDao import org.tasks.data.entity.Alarm.Companion.TYPE_SNOOZE
import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavTaskMaker import org.tasks.makers.CaldavTaskMaker
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.CaldavTaskMaker.newCaldavTask
import org.tasks.makers.TagDataMaker.NAME
import org.tasks.makers.TagDataMaker.newTagData
import org.tasks.makers.TagMaker.TAGDATA
import org.tasks.makers.TagMaker.TASK
import org.tasks.makers.TagMaker.newTag
import org.tasks.makers.TaskMaker import org.tasks.makers.TaskMaker
import org.tasks.makers.TaskMaker.COLLAPSED import org.tasks.makers.TaskMaker.COLLAPSED
import org.tasks.makers.TaskMaker.ORDER import org.tasks.makers.TaskMaker.ORDER
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import org.tasks.time.DateTime import org.tasks.time.DateTime
import java.util.* import java.util.TimeZone
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -90,8 +90,7 @@ class OpenTasksPropertiesTests : OpenTasksTest() {
@Test @Test
fun matchExistingTag() = runBlocking { fun matchExistingTag() = runBlocking {
val (_, list) = withVtodo(ONE_TAG) val (_, list) = withVtodo(ONE_TAG)
val tag = newTagData(with(NAME, "Tag1")) val tag = TagData(name = "Tag1").let { it.copy(id = tagDataDao.insert(it)) }
tagDataDao.createNew(tag)
synchronizer.sync() synchronizer.sync()
@ -213,7 +212,14 @@ class OpenTasksPropertiesTests : OpenTasksTest() {
?.let { taskDao.fetch(it.task) } ?.let { taskDao.fetch(it.task) }
assertEquals( assertEquals(
listOf(Alarm(task!!.id, 1612972355000, TYPE_SNOOZE).apply { id = 1 }), listOf(
Alarm(
id = 1,
task = task!!.id,
time = 1612972355000,
type = TYPE_SNOOZE
)
),
alarmDao.getAlarms(task.id) alarmDao.getAlarms(task.id)
) )
} }
@ -222,7 +228,13 @@ class OpenTasksPropertiesTests : OpenTasksTest() {
fun pushSnoozeTime() = withTZ(CHICAGO) { fun pushSnoozeTime() = withTZ(CHICAGO) {
val (listId, list) = openTaskDao.insertList() val (listId, list) = openTaskDao.insertList()
val taskId = taskDao.createNew(newTask()) val taskId = taskDao.createNew(newTask())
alarmDao.insert(Alarm(taskId, DateTime(2021, 2, 4, 13, 30).millis, TYPE_SNOOZE)) alarmDao.insert(
Alarm(
task = taskId,
time = DateTime(2021, 2, 4, 13, 30).millis,
type = TYPE_SNOOZE
)
)
caldavDao.insert(newCaldavTask( caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid), with(CALENDAR, list.uuid),
@ -241,7 +253,13 @@ class OpenTasksPropertiesTests : OpenTasksTest() {
fun dontPushLapsedSnoozeTime() = withTZ(CHICAGO) { fun dontPushLapsedSnoozeTime() = withTZ(CHICAGO) {
val (listId, list) = openTaskDao.insertList() val (listId, list) = openTaskDao.insertList()
val taskId = taskDao.createNew(newTask()) val taskId = taskDao.createNew(newTask())
alarmDao.insert(Alarm(taskId, DateTime(2021, 2, 4, 13, 30).millis, TYPE_SNOOZE)) alarmDao.insert(
Alarm(
task = taskId,
time = DateTime(2021, 2, 4, 13, 30).millis,
type = TYPE_SNOOZE
)
)
caldavDao.insert(newCaldavTask( caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid), with(CALENDAR, list.uuid),
@ -257,17 +275,18 @@ class OpenTasksPropertiesTests : OpenTasksTest() {
} }
@Test @Test
fun removeSnoozeTime() = runBlocking { fun removeSnoozeTime() = withTZ(CHICAGO) {
val (listId, list) = withVtodo(SNOOZED) val (listId, list) = withVtodo(SNOOZED)
synchronizer.sync() synchronizer.sync()
val task = caldavDao.getTaskByRemoteId(list.uuid!!, "4CBBC669-70E3-474D-A0A3-0FC42A14A5A5") val task = caldavDao.getTaskByRemoteId(list.uuid!!, "4CBBC669-70E3-474D-A0A3-0FC42A14A5A5")
?: throw IllegalStateException("Missing task") ?: throw IllegalStateException("Missing task")
val snooze = alarmDao.getSnoozed(listOf(task.task)) assertEquals(
assertEquals(1, snooze.size) listOf(Alarm(1, task.id, DateTime(2021, 2, 10, 9, 52, 35).millis, TYPE_SNOOZE)),
alarmDao.delete(snooze.first()) alarmDao.getAlarms(1)
assertTrue(alarmDao.getSnoozed(listOf(task.task)).isEmpty()) )
alarmDao.deleteSnoozed(listOf(1))
taskDao.touch(task.task) taskDao.touch(task.task)
synchronizer.sync() synchronizer.sync()
@ -281,9 +300,9 @@ class OpenTasksPropertiesTests : OpenTasksTest() {
} }
private suspend fun insertTag(task: Task, name: String) = private suspend fun insertTag(task: Task, name: String) =
newTagData(with(NAME, name)) TagData(name = name)
.apply { tagDataDao.createNew(this) } .apply { tagDataDao.insert(this) }
.let { tagDao.insert(newTag(with(TASK, task), with(TAGDATA, it))) } .let { tagDao.insert(Tag(task = task.id, taskUid = task.uuid, tagUid = it.remoteId)) }
companion object { companion object {
private val CHICAGO = TimeZone.getTimeZone("America/Chicago") private val CHICAGO = TimeZone.getTimeZone("America/Chicago")

@ -4,11 +4,13 @@ import com.natpryce.makeiteasy.MakeItEasy.with
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.* import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS import org.tasks.data.entity.CaldavAccount.Companion.TYPE_OPENTASKS
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
@ -38,10 +40,12 @@ class OpenTasksSynchronizerTest : OpenTasksTest() {
@Test @Test
fun deleteRemovedAccounts() = runBlocking { fun deleteRemovedAccounts() = runBlocking {
caldavDao.insert(CaldavAccount().apply { caldavDao.insert(
uuid = "bitfire.at.davdroid:test_account" CaldavAccount(
accountType = TYPE_OPENTASKS uuid = "bitfire.at.davdroid:test_account",
}) accountType = TYPE_OPENTASKS,
)
)
synchronizer.sync() synchronizer.sync()

@ -3,8 +3,8 @@ package org.tasks.opentasks
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import org.junit.Before import org.junit.Before
import org.tasks.R import org.tasks.R
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import javax.inject.Inject import javax.inject.Inject

@ -4,13 +4,13 @@ import android.content.ContentProviderResult
import android.content.Context import android.content.Context
import at.bitfire.ical4android.BatchOperation import at.bitfire.ical4android.BatchOperation
import at.bitfire.ical4android.Task import at.bitfire.ical4android.Task
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.dmfs.tasks.contract.TaskContract import org.dmfs.tasks.contract.TaskContract
import org.dmfs.tasks.contract.TaskContract.TaskListColumns.ACCESS_LEVEL_OWNER import org.dmfs.tasks.contract.TaskContract.TaskListColumns.ACCESS_LEVEL_OWNER
import org.tasks.caldav.iCalendar import org.tasks.caldav.iCalendar
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.MyAndroidTask import org.tasks.data.MyAndroidTask
import org.tasks.data.OpenTaskDao import org.tasks.data.OpenTaskDao
import javax.inject.Inject import javax.inject.Inject
@ -20,11 +20,11 @@ class TestOpenTaskDao @Inject constructor(
private val caldavDao: CaldavDao private val caldavDao: CaldavDao
) : OpenTaskDao(context, caldavDao) { ) : OpenTaskDao(context, caldavDao) {
suspend fun insertList( suspend fun insertList(
name: String = DEFAULT_LIST, name: String = DEFAULT_LIST,
type: String = DEFAULT_TYPE, type: String = DEFAULT_TYPE,
account: String = DEFAULT_ACCOUNT, account: String = DEFAULT_ACCOUNT,
url: String = UUIDHelper.newUUID(), url: String = UUIDHelper.newUUID(),
accessLevel: Int = ACCESS_LEVEL_OWNER, accessLevel: Int = ACCESS_LEVEL_OWNER,
): Pair<Long, CaldavCalendar> { ): Pair<Long, CaldavCalendar> {
val uri = taskLists.buildUpon() val uri = taskLists.buildUpon()
.appendQueryParameter(TaskContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(TaskContract.CALLER_IS_SYNCADAPTER, "true")

@ -3,9 +3,9 @@ package org.tasks.preferences
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.todoroo.astrid.data.Task.Companion.NOTIFY_AFTER_DEADLINE import org.tasks.data.entity.Task.Companion.NOTIFY_AFTER_DEADLINE
import com.todoroo.astrid.data.Task.Companion.NOTIFY_AT_DEADLINE import org.tasks.data.entity.Task.Companion.NOTIFY_AT_DEADLINE
import com.todoroo.astrid.data.Task.Companion.NOTIFY_AT_START import org.tasks.data.entity.Task.Companion.NOTIFY_AT_START
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test

@ -11,7 +11,8 @@ import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.time.DateTime import org.tasks.time.DateTime
import java.text.ParseException import java.text.ParseException
import java.util.* import java.util.Locale
import java.util.TimeZone
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -84,7 +85,7 @@ class RepeatRuleToStringTest : InjectingTestCase() {
Freeze.freezeAt(DateTime(2021, 1, 4)) { Freeze.freezeAt(DateTime(2021, 1, 4)) {
withTZ(BERLIN) { withTZ(BERLIN) {
assertEquals( assertEquals(
"Repeats daily until February 23", "Repeats daily, ends on February 23",
toString("RRULE:FREQ=DAILY;UNTIL=20210223;INTERVAL=1") toString("RRULE:FREQ=DAILY;UNTIL=20210223;INTERVAL=1")
) )
} }
@ -96,7 +97,7 @@ class RepeatRuleToStringTest : InjectingTestCase() {
Freeze.freezeAt(DateTime(2021, 1, 4)) { Freeze.freezeAt(DateTime(2021, 1, 4)) {
withTZ(LONDON) { withTZ(LONDON) {
assertEquals( assertEquals(
"Repeats daily until February 23", "Repeats daily, ends on February 23",
toString("RRULE:FREQ=DAILY;UNTIL=20210223;INTERVAL=1") toString("RRULE:FREQ=DAILY;UNTIL=20210223;INTERVAL=1")
) )
} }
@ -108,7 +109,7 @@ class RepeatRuleToStringTest : InjectingTestCase() {
Freeze.freezeAt(DateTime(2021, 1, 4)) { Freeze.freezeAt(DateTime(2021, 1, 4)) {
withTZ(NEW_YORK) { withTZ(NEW_YORK) {
assertEquals( assertEquals(
"Repeats daily until February 23", "Repeats daily, ends on February 23",
toString("RRULE:FREQ=DAILY;UNTIL=20210223;INTERVAL=1") toString("RRULE:FREQ=DAILY;UNTIL=20210223;INTERVAL=1")
) )
} }

@ -3,9 +3,9 @@ package org.tasks.ui.editviewmodel
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import com.todoroo.astrid.activity.TaskEditFragment import com.todoroo.astrid.activity.TaskEditFragment
import com.todoroo.astrid.alarms.AlarmService import com.todoroo.astrid.alarms.AlarmService
import com.todoroo.astrid.dao.Database import org.tasks.data.db.Database
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.gcal.GCalHelper import com.todoroo.astrid.gcal.GCalHelper
import com.todoroo.astrid.service.TaskCompleter import com.todoroo.astrid.service.TaskCompleter
import com.todoroo.astrid.service.TaskDeleter import com.todoroo.astrid.service.TaskDeleter
@ -15,10 +15,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tasks.calendars.CalendarEventProvider import org.tasks.calendars.CalendarEventProvider
import org.tasks.data.AlarmDao import org.tasks.data.dao.AlarmDao
import org.tasks.data.LocationDao import org.tasks.data.dao.LocationDao
import org.tasks.data.TagDataDao import org.tasks.data.dao.TagDataDao
import org.tasks.data.UserActivityDao import org.tasks.data.dao.UserActivityDao
import org.tasks.data.getLocation
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.location.GeofenceApi import org.tasks.location.GeofenceApi
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
@ -64,20 +65,20 @@ open class BaseTaskEditViewModelTest : InjectingTestCase() {
calendarEventProvider, calendarEventProvider,
gCalHelper, gCalHelper,
taskMover, taskMover,
db.locationDao, db.locationDao(),
geofenceApi, geofenceApi,
db.tagDao, db.tagDao(),
db.tagDataDao, db.tagDataDao(),
preferences, preferences,
db.googleTaskDao, db.googleTaskDao(),
db.caldavDao, db.caldavDao(),
taskCompleter, taskCompleter,
alarmService, alarmService,
MutableSharedFlow(), MutableSharedFlow(),
MutableSharedFlow(), MutableSharedFlow(),
userActivityDao = userActivityDao, userActivityDao = userActivityDao,
taskAttachmentDao = db.taskAttachmentDao, taskAttachmentDao = db.taskAttachmentDao(),
alarmDao = db.alarmDao, alarmDao = db.alarmDao(),
) )
} }

@ -1,7 +1,7 @@
package org.tasks.ui.editviewmodel package org.tasks.ui.editviewmodel
import com.natpryce.makeiteasy.MakeItEasy import com.natpryce.makeiteasy.MakeItEasy
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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 org.junit.Assert import org.junit.Assert

@ -1,80 +1,79 @@
package org.tasks.ui.editviewmodel package org.tasks.ui.editviewmodel
import com.todoroo.astrid.data.Task import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.service.TaskCreator.Companion.setDefaultReminders
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.* import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.data.Alarm import org.tasks.R
import org.tasks.data.Alarm.Companion.whenOverdue import org.tasks.data.createDueDate
import org.tasks.data.entity.Alarm
import org.tasks.data.entity.Alarm.Companion.whenDue
import org.tasks.data.entity.Alarm.Companion.whenOverdue
import org.tasks.data.entity.Alarm.Companion.whenStarted
import org.tasks.data.entity.Task
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.makers.TaskMaker.DUE_TIME
import org.tasks.makers.TaskMaker.START_DATE
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils2.currentTimeMillis
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@HiltAndroidTest @HiltAndroidTest
class ReminderTests : BaseTaskEditViewModelTest() { class ReminderTests : BaseTaskEditViewModelTest() {
@Test @Test
fun whenStartReminder() = runBlocking { fun whenStartReminder() = runBlocking {
val task = newTask() preferences.setStringSet(
task.defaultReminders(Task.NOTIFY_AT_START) R.string.p_default_reminders_key,
setup(task) hashSetOf(Task.NOTIFY_AT_START.toString())
viewModel.setStartDate(
Task.createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME,
currentTimeMillis()
)
) )
val task = newTask(with(START_DATE, DateTime()))
task.setDefaultReminders(preferences)
save() setup(task)
assertEquals( assertEquals(
listOf(Alarm(1, 0, Alarm.TYPE_REL_START).apply { id = 1 }), listOf(Alarm(type = Alarm.TYPE_REL_START)),
alarmDao.getAlarms(task.id) viewModel.selectedAlarms.value
) )
} }
@Test @Test
fun whenDueReminder() = runBlocking { fun whenDueReminder() = runBlocking {
val task = newTask() preferences.setStringSet(
task.defaultReminders(Task.NOTIFY_AT_DEADLINE) R.string.p_default_reminders_key,
setup(task) hashSetOf(Task.NOTIFY_AT_DEADLINE.toString())
viewModel.setDueDate(
Task.createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME,
currentTimeMillis()
)
) )
val task = newTask(with(DUE_TIME, DateTime()))
task.setDefaultReminders(preferences)
save() setup(task)
assertEquals( assertEquals(
listOf(Alarm(1, 0, Alarm.TYPE_REL_END).apply { id = 1 }), listOf(Alarm(type = Alarm.TYPE_REL_END)),
alarmDao.getAlarms(task.id) viewModel.selectedAlarms.value
) )
} }
@Test @Test
fun whenOverDueReminder() = runBlocking { fun whenOverDueReminder() = runBlocking {
val task = newTask() preferences.setStringSet(
task.defaultReminders(Task.NOTIFY_AFTER_DEADLINE) R.string.p_default_reminders_key,
setup(task) hashSetOf(Task.NOTIFY_AFTER_DEADLINE.toString())
viewModel.setDueDate(
Task.createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME,
currentTimeMillis()
)
) )
val task = newTask(with(DUE_TIME, DateTime()))
task.setDefaultReminders(preferences)
save() setup(task)
assertEquals( assertEquals(
listOf(whenOverdue(1).apply { id = 1 }), listOf(whenOverdue(0)),
alarmDao.getAlarms(task.id) viewModel.selectedAlarms.value
) )
} }
@ -129,4 +128,67 @@ class ReminderTests : BaseTaskEditViewModelTest() {
assertFalse(taskDao.fetch(task.id)!!.isNotifyModeFive) assertFalse(taskDao.fetch(task.id)!!.isNotifyModeFive)
assertTrue(taskDao.fetch(task.id)!!.isNotifyModeNonstop) assertTrue(taskDao.fetch(task.id)!!.isNotifyModeNonstop)
} }
}
@Test
fun noDefaultRemindersWithNoDates() = runBlocking {
val task = newTask()
task.setDefaultReminders(preferences)
setup(task)
save()
assertTrue(alarmDao.getAlarms(task.id).isEmpty())
}
@Test
fun addDefaultRemindersWhenAddingDueDate() = runBlocking {
preferences.setStringSet(
R.string.p_default_reminders_key,
hashSetOf(
Task.NOTIFY_AT_DEADLINE.toString(),
Task.NOTIFY_AFTER_DEADLINE.toString(),
)
)
val task = newTask()
setup(task)
viewModel.setDueDate(
createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME,
currentTimeMillis()
)
)
save()
assertEquals(
listOf(whenDue(1).copy(id = 1), whenOverdue(1).copy(id = 2)),
alarmDao.getAlarms(task.id)
)
}
@Test
fun addDefaultRemindersWhenAddingStartDate() = runBlocking {
preferences.setStringSet(
R.string.p_default_reminders_key,
hashSetOf(Task.NOTIFY_AT_START.toString())
)
val task = newTask()
setup(task)
viewModel.setStartDate(
createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME,
currentTimeMillis()
)
)
save()
assertEquals(
listOf(whenStarted(1).copy(id = 1)),
alarmDao.getAlarms(task.id)
)
}
}

@ -1,6 +1,6 @@
package org.tasks.ui.editviewmodel package org.tasks.ui.editviewmodel
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
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

@ -1,8 +1,7 @@
package org.tasks.ui.editviewmodel package org.tasks.ui.editviewmodel
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.service.TaskDeleter import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
@ -14,11 +13,12 @@ import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.data.DeletionDao import org.tasks.data.dao.DeletionDao
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.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.ui.TaskListViewModel import org.tasks.ui.TaskListViewModel
import javax.inject.Inject import javax.inject.Inject
@ -53,7 +53,7 @@ class TaskListViewModelTest : InjectingTestCase() {
@Test @Test
fun clearCompletedTask() = runBlocking { fun clearCompletedTask() = runBlocking {
val task = taskDao.createNew( val task = taskDao.createNew(
Task(completionDate = now()) Task(completionDate = currentTimeMillis())
) )
clearCompleted() clearCompleted()
@ -71,7 +71,7 @@ class TaskListViewModelTest : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )
@ -91,7 +91,7 @@ class TaskListViewModelTest : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )
@ -109,7 +109,7 @@ class TaskListViewModelTest : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )
@ -123,7 +123,7 @@ class TaskListViewModelTest : InjectingTestCase() {
val grandparent = taskDao.createNew( val grandparent = taskDao.createNew(
Task( Task(
recurrence = "RRULE:FREQ=DAILY;INTERVAL=1", recurrence = "RRULE:FREQ=DAILY;INTERVAL=1",
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )
val parent = taskDao.createNew( val parent = taskDao.createNew(
@ -132,7 +132,7 @@ class TaskListViewModelTest : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )
@ -148,7 +148,7 @@ class TaskListViewModelTest : InjectingTestCase() {
val child = taskDao.createNew( val child = taskDao.createNew(
Task( Task(
parent = parent, parent = parent,
completionDate = now(), completionDate = currentTimeMillis(),
) )
) )

@ -1,7 +1,7 @@
package org.tasks.ui.editviewmodel package org.tasks.ui.editviewmodel
import com.natpryce.makeiteasy.MakeItEasy.with import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.data.Task.Priority.Companion.HIGH import org.tasks.data.entity.Task.Priority.Companion.HIGH
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

@ -7,7 +7,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.injection.InjectingTestCase import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences

@ -1,7 +1,7 @@
package org.tasks.caldav package org.tasks.caldav
import androidx.test.annotation.UiThreadTest import androidx.test.annotation.UiThreadTest
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
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
@ -9,7 +9,7 @@ import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.tasks.R import org.tasks.R
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.injection.ProductionModule import org.tasks.injection.ProductionModule
import javax.inject.Inject import javax.inject.Inject
@ -25,10 +25,8 @@ class CaldavSubscriptionTest : CaldavTest() {
inventory.clear() inventory.clear()
inventory.add(emptyList()) inventory.add(emptyList())
account = CaldavAccount().apply { account = CaldavAccount(uuid = UUIDHelper.newUUID())
uuid = UUIDHelper.newUUID() .let { it.copy(id = caldavDao.insert(it)) }
id = caldavDao.insert(this)
}
synchronizer.sync(account) synchronizer.sync(account)

@ -5,8 +5,8 @@ import com.facebook.flipper.plugins.network.NetworkReporter
import com.facebook.flipper.plugins.network.NetworkReporter.ResponseInfo import com.facebook.flipper.plugins.network.NetworkReporter.ResponseInfo
import com.google.api.client.http.* import com.google.api.client.http.*
import com.google.api.client.json.GenericJson import com.google.api.client.json.GenericJson
import com.todoroo.andlib.utility.DateUtilities import org.tasks.data.UUIDHelper
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.time.DateTimeUtils2.currentTimeMillis
import timber.log.Timber import timber.log.Timber
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
@ -18,12 +18,12 @@ internal class FlipperHttpInterceptor<T>(private val plugin: NetworkFlipperPlugi
private set private set
override fun intercept(request: HttpRequest) { override fun intercept(request: HttpRequest) {
plugin.reportRequest(toRequestInfo(request, DateUtilities.now())) plugin.reportRequest(toRequestInfo(request, currentTimeMillis()))
} }
@Throws(IOException::class) @Throws(IOException::class)
override fun interceptResponse(response: HttpResponse) { override fun interceptResponse(response: HttpResponse) {
plugin.reportResponse(toResponseInfo(response, DateUtilities.now())) plugin.reportResponse(toResponseInfo(response, currentTimeMillis()))
} }
@Throws(IOException::class) @Throws(IOException::class)

@ -5,7 +5,6 @@ import androidx.annotation.StringRes
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import at.bitfire.cert4android.CustomCertManager.Companion.resetCertificates import at.bitfire.cert4android.CustomCertManager.Companion.resetCertificates
import com.todoroo.andlib.utility.DateUtilities.now
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.R import org.tasks.R
@ -14,6 +13,7 @@ import org.tasks.billing.Inventory
import org.tasks.extensions.Context.toast import org.tasks.extensions.Context.toast
import org.tasks.injection.InjectingPreferenceFragment import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.min import kotlin.math.min
@ -62,7 +62,7 @@ class Debug : InjectingPreferenceFragment() {
findPreference(R.string.debug_clear_hints).setOnPreferenceClickListener { findPreference(R.string.debug_clear_hints).setOnPreferenceClickListener {
preferences.installDate = preferences.installDate =
min(preferences.installDate, now() - TimeUnit.DAYS.toMillis(14)) min(preferences.installDate, currentTimeMillis() - TimeUnit.DAYS.toMillis(14))
preferences.lastSubscribeRequest = 0L preferences.lastSubscribeRequest = 0L
preferences.lastReviewRequest = 0L preferences.lastReviewRequest = 0L
preferences.shownBeastModeHint = false preferences.shownBeastModeHint = false

@ -7,11 +7,11 @@ import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
import com.todoroo.andlib.utility.DateUtilities.now
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.R import org.tasks.R
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -64,14 +64,14 @@ class Firebase @Inject constructor(
} }
private val installCooldown: Boolean private val installCooldown: Boolean
get() = preferences.installDate + days("install_cooldown", 14L) > now() get() = preferences.installDate + days("install_cooldown", 14L) > currentTimeMillis()
val reviewCooldown: Boolean val reviewCooldown: Boolean
get() = installCooldown || preferences.lastReviewRequest + days("review_cooldown", 30L) > now() get() = installCooldown || preferences.lastReviewRequest + days("review_cooldown", 30L) > currentTimeMillis()
val subscribeCooldown: Boolean val subscribeCooldown: Boolean
get() = installCooldown get() = installCooldown
|| preferences.lastSubscribeRequest + days("subscribe_cooldown", 30L) > now() || preferences.lastSubscribeRequest + days("subscribe_cooldown", 30L) > currentTimeMillis()
val moreOptionsBadge: Boolean val moreOptionsBadge: Boolean
get() = remoteConfig?.getBoolean("more_options_badge") ?: false get() = remoteConfig?.getBoolean("more_options_badge") ?: false

@ -1,16 +1,23 @@
package org.tasks.billing package org.tasks.billing
import com.android.billingclient.api.Purchase import com.android.billingclient.api.Purchase
import com.google.gson.GsonBuilder import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.tasks.billing.BillingClientImpl.Companion.STATE_PURCHASED import org.tasks.billing.BillingClientImpl.Companion.STATE_PURCHASED
import java.util.regex.Pattern import java.util.regex.Pattern
class Purchase(private val purchase: Purchase) { class Purchase(private val purchase: Purchase) {
constructor(json: String?) : this(GsonBuilder().create().fromJson<Purchase>(json, Purchase::class.java)) constructor(json: String) : this(
Json.parseToJsonElement(json).jsonObject.let {
Purchase(it["zza"]!!.jsonPrimitive.content, it["zzb"]!!.jsonPrimitive.content)
}
)
fun toJson(): String { fun toJson(): String {
return GsonBuilder().create().toJson(purchase) return Json.encodeToString(mapOf("zza" to purchase.originalJson, "zzb" to purchase.signature))
} }
override fun toString(): String { override fun toString(): String {

@ -5,11 +5,11 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import com.google.android.gms.location.Geofence import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingEvent import com.google.android.gms.location.GeofencingEvent
import com.todoroo.andlib.utility.DateUtilities
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.tasks.Notifier import org.tasks.Notifier
import org.tasks.data.LocationDao import org.tasks.data.dao.LocationDao
import org.tasks.injection.InjectingJobIntentService import org.tasks.injection.InjectingJobIntentService
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -45,9 +45,9 @@ class GoogleGeofenceTransitionIntentService : InjectingJobIntentService() {
return return
} }
val geofences = if (arrival) { val geofences = if (arrival) {
locationDao.getArrivalGeofences(place.uid!!, DateUtilities.now()) locationDao.getArrivalGeofences(place.uid!!, currentTimeMillis())
} else { } else {
locationDao.getDepartureGeofences(place.uid!!, DateUtilities.now()) locationDao.getDepartureGeofences(place.uid!!, currentTimeMillis())
} }
notifier.triggerNotifications(place.id, geofences, arrival) notifier.triggerNotifications(place.id, geofences, arrival)
} catch (e: Exception) { } catch (e: Exception) {

@ -10,7 +10,7 @@ import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.* import com.google.android.gms.maps.model.*
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.R import org.tasks.R
import org.tasks.data.Place import org.tasks.data.entity.Place
import org.tasks.location.MapFragment.MapFragmentCallback import org.tasks.location.MapFragment.MapFragmentCallback
import javax.inject.Inject import javax.inject.Inject

@ -12,7 +12,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tasks.data.MergedGeofence import org.tasks.data.MergedGeofence
import org.tasks.data.Place import org.tasks.data.entity.Place
import javax.inject.Inject import javax.inject.Inject
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine

@ -8,12 +8,12 @@ import com.google.android.gms.common.GoogleApiAvailability.getInstance
import com.google.android.play.core.ktx.launchReview import com.google.android.play.core.ktx.launchReview
import com.google.android.play.core.ktx.requestReview import com.google.android.play.core.ktx.requestReview
import com.google.android.play.core.review.ReviewManagerFactory import com.google.android.play.core.review.ReviewManagerFactory
import com.todoroo.andlib.utility.DateUtilities.now
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.R import org.tasks.R
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
class PlayServices @Inject constructor( class PlayServices @Inject constructor(
@ -33,7 +33,7 @@ class PlayServices @Inject constructor(
val request = requestReview() val request = requestReview()
launchReview(activity, request) launchReview(activity, request)
} }
preferences.lastReviewRequest = now() preferences.lastReviewRequest = currentTimeMillis()
firebase.logEvent(R.string.event_request_review) firebase.logEvent(R.string.event_request_review)
} catch (e: Exception) { } catch (e: Exception) {
firebase.reportException(e) firebase.reportException(e)

@ -310,12 +310,15 @@
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/scrollable_widget_provider_info"/> android:resource="@xml/scrollable_widget_provider_info"/>
<meta-data
android:name="com.samsung.android.appwidget.provider"
android:resource="@xml/samsung_scrollable_flex_window_widget_meta_info"/>
</receiver> </receiver>
<!-- ======================================================== Services = --> <!-- ======================================================== Services = -->
<service <service
android:name=".widget.ScrollableWidgetUpdateService" android:name=".widget.TasksWidgetAdapter"
android:permission="android.permission.BIND_REMOTEVIEWS"/> android:permission="android.permission.BIND_REMOTEVIEWS"/>
<service <service
@ -529,10 +532,6 @@
android:value="org.tasks.dashclock.DashClockSettings"/> android:value="org.tasks.dashclock.DashClockSettings"/>
</service> </service>
<service
android:exported="false"
android:name=".jobs.NotificationService"/>
<activity <activity
android:exported="true" android:exported="true"
android:name=".dashclock.DashClockSettings"/> android:name=".dashclock.DashClockSettings"/>
@ -598,6 +597,8 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name="org.tasks.jobs.NotificationReceiver" />
<activity <activity
android:name=".auth.MicrosoftAuthenticationActivity" android:name=".auth.MicrosoftAuthenticationActivity"
android:theme="@style/TranslucentDialog"/> android:theme="@style/TranslucentDialog"/>

@ -140,8 +140,12 @@ public class AndroidUtilities {
return !atLeastOreo(); return !atLeastOreo();
} }
public static boolean preS() {
return !atLeastS();
}
public static boolean preTiramisu() { public static boolean preTiramisu() {
return VERSION.SDK_INT < VERSION_CODES.TIRAMISU; return !atLeastTiramisu();
} }
public static boolean preUpsideDownCake() { public static boolean preUpsideDownCake() {
@ -156,6 +160,10 @@ public class AndroidUtilities {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
} }
public static boolean atLeastOreoMR1() {
return Build.VERSION.SDK_INT >= VERSION_CODES.O_MR1;
}
public static boolean atLeastP() { public static boolean atLeastP() {
return VERSION.SDK_INT >= Build.VERSION_CODES.P; return VERSION.SDK_INT >= Build.VERSION_CODES.P;
} }

@ -7,14 +7,13 @@
package com.todoroo.andlib.utility; package com.todoroo.andlib.utility;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.content.Context; import android.content.Context;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.todoroo.astrid.data.Task; import org.tasks.data.entity.Task;
import org.tasks.BuildConfig; import org.tasks.BuildConfig;
import org.tasks.R; import org.tasks.R;
@ -38,11 +37,6 @@ public class DateUtilities {
static Boolean is24HourOverride = null; static Boolean is24HourOverride = null;
/** Returns unixtime for current time */
public static long now() {
return currentTimeMillis();
}
/* ====================================================================== /* ======================================================================
* =========================================================== formatters * =========================================================== formatters
* ====================================================================== */ * ====================================================================== */

@ -12,15 +12,9 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.compose.material.MaterialTheme import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.runtime.LaunchedEffect import androidx.compose.foundation.layout.mandatorySystemGestures
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.core.content.IntentCompat.getParcelableExtra import androidx.core.content.IntentCompat.getParcelableExtra
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.flowWithLifecycle
@ -32,7 +26,6 @@ import com.todoroo.astrid.activity.TaskEditFragment.Companion.newTaskEditFragmen
import com.todoroo.astrid.adapter.SubheaderClickHandler import com.todoroo.astrid.adapter.SubheaderClickHandler
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskCreator import com.todoroo.astrid.service.TaskCreator
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -46,34 +39,25 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tasks.BuildConfig import org.tasks.BuildConfig
import org.tasks.R import org.tasks.R
import org.tasks.Tasks.Companion.IS_GENERIC
import org.tasks.activities.NavigationDrawerCustomization
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.billing.PurchaseActivity
import org.tasks.compose.collectAsStateLifecycleAware import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.drawer.DrawerAction import org.tasks.compose.drawer.TasksMenu
import org.tasks.compose.drawer.DrawerItem import org.tasks.data.dao.AlarmDao
import org.tasks.compose.drawer.ModalBottomSheet import org.tasks.data.dao.LocationDao
import org.tasks.compose.drawer.SheetState import org.tasks.data.entity.Place
import org.tasks.compose.drawer.SheetValue import org.tasks.data.dao.TagDataDao
import org.tasks.compose.drawer.TaskListDrawer import org.tasks.data.entity.Task
import org.tasks.data.AlarmDao import org.tasks.data.getLocation
import org.tasks.data.LocationDao
import org.tasks.data.Place
import org.tasks.data.TagDataDao
import org.tasks.databinding.TaskListActivityBinding import org.tasks.databinding.TaskListActivityBinding
import org.tasks.dialogs.NewFilterDialog import org.tasks.dialogs.NewFilterDialog
import org.tasks.dialogs.WhatsNewDialog import org.tasks.dialogs.WhatsNewDialog
import org.tasks.extensions.Context.nightMode import org.tasks.extensions.Context.nightMode
import org.tasks.extensions.Context.openUri
import org.tasks.extensions.hideKeyboard import org.tasks.extensions.hideKeyboard
import org.tasks.filters.FilterProvider import org.tasks.filters.FilterProvider
import org.tasks.filters.PlaceFilter import org.tasks.filters.PlaceFilter
import org.tasks.location.LocationPickerActivity.Companion.EXTRA_PLACE import org.tasks.location.LocationPickerActivity.Companion.EXTRA_PLACE
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.HelpAndFeedback
import org.tasks.preferences.MainPreferences
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.themes.ColorProvider import org.tasks.themes.ColorProvider
import org.tasks.themes.Theme import org.tasks.themes.Theme
@ -111,7 +95,6 @@ class MainActivity : AppCompatActivity() {
/** @see android.app.Activity.onCreate /** @see android.app.Activity.onCreate
*/ */
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
theme.applyTheme(this) theme.applyTheme(this)
@ -126,113 +109,29 @@ class MainActivity : AppCompatActivity() {
val state = viewModel.state.collectAsStateLifecycleAware().value val state = viewModel.state.collectAsStateLifecycleAware().value
if (state.drawerOpen) { if (state.drawerOpen) {
MdcTheme { MdcTheme {
var expanded by remember { mutableStateOf(false) } TasksMenu(
val skipPartiallyExpanded = remember(expanded) { bottomPadding = WindowInsets.mandatorySystemGestures
expanded || preferences.isTopAppBar .asPaddingValues()
} .calculateBottomPadding(),
val sheetState = rememberSaveable( items = state.drawerItems,
skipPartiallyExpanded, begForMoney = state.begForMoney,
saver = SheetState.Saver( isTopAppBar = preferences.isTopAppBar,
skipPartiallyExpanded = skipPartiallyExpanded, setFilter = { viewModel.setFilter(it) },
confirmValueChange = { true }, toggleCollapsed = { viewModel.toggleCollapsed(it) },
) addFilter = {
) { val rc = it.addIntentRc
SheetState( if (rc == FilterProvider.REQUEST_NEW_FILTER) {
skipPartiallyExpanded = skipPartiallyExpanded, NewFilterDialog.newFilterDialog().show(
initialValue = if (skipPartiallyExpanded) SheetValue.Expanded else SheetValue.PartiallyExpanded, supportFragmentManager,
confirmValueChange = { true }, SubheaderClickHandler.FRAG_TAG_NEW_FILTER
skipHiddenState = false, )
) } else {
} val intent = it.addIntent ?: return@TasksMenu
LaunchedEffect(sheetState.currentValue) { startActivityForResult(intent, rc)
if (sheetState.currentValue == SheetValue.Expanded) { }
expanded = true },
} dismiss = { viewModel.setDrawerOpen(false) },
} )
ModalBottomSheet(
sheetState = sheetState,
containerColor = MaterialTheme.colors.surface,
onDismissRequest = { viewModel.setDrawerOpen(false) }
) {
val scope = rememberCoroutineScope()
TaskListDrawer(
begForMoney = state.begForMoney,
filters = state.drawerItems,
onClick = {
when (it) {
is DrawerItem.Filter -> {
viewModel.setFilter(it.type())
scope.launch(Dispatchers.Default) {
sheetState.hide()
viewModel.setDrawerOpen(false)
}
}
is DrawerItem.Header -> {
viewModel.toggleCollapsed(it.type())
}
}
},
onAddClick = {
scope.launch(Dispatchers.Default) {
sheetState.hide()
viewModel.setDrawerOpen(false)
val subheaderType = it.type()
val rc = subheaderType.addIntentRc
if (rc == FilterProvider.REQUEST_NEW_FILTER) {
NewFilterDialog.newFilterDialog().show(
supportFragmentManager,
SubheaderClickHandler.FRAG_TAG_NEW_FILTER
)
} else {
val intent = subheaderType.addIntent ?: return@launch
startActivityForResult(intent, rc)
}
}
},
onDrawerAction = {
viewModel.setDrawerOpen(false)
when (it) {
DrawerAction.PURCHASE ->
if (IS_GENERIC)
openUri(R.string.url_donate)
else
startActivity(
Intent(
this@MainActivity,
PurchaseActivity::class.java
)
)
DrawerAction.CUSTOMIZE_DRAWER ->
startActivity(
Intent(
this@MainActivity,
NavigationDrawerCustomization::class.java
)
)
DrawerAction.SETTINGS ->
settingsRequest.launch(
Intent(
this@MainActivity,
MainPreferences::class.java
)
)
DrawerAction.HELP_AND_FEEDBACK ->
startActivity(
Intent(
this@MainActivity,
HelpAndFeedback::class.java
)
)
}
},
onErrorClick = {
startActivity(Intent(this@MainActivity, MainPreferences::class.java))
},
)
}
} }
} }
} }

@ -11,12 +11,9 @@ import com.todoroo.astrid.activity.MainActivity.Companion.OPEN_FILTER
import com.todoroo.astrid.api.CaldavFilter import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.CustomFilter import com.todoroo.astrid.api.CustomFilter
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.api.TagFilter import com.todoroo.astrid.api.TagFilter
import com.todoroo.astrid.data.Task
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
@ -31,8 +28,11 @@ import org.tasks.R
import org.tasks.Tasks.Companion.IS_GENERIC import org.tasks.Tasks.Companion.IS_GENERIC
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.compose.drawer.DrawerItem import org.tasks.compose.drawer.DrawerItem
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.TaskDao import org.tasks.data.NO_COUNT
import org.tasks.data.entity.Task
import org.tasks.data.dao.TaskDao
import org.tasks.data.count
import org.tasks.filters.FilterProvider import org.tasks.filters.FilterProvider
import org.tasks.filters.NavigationDrawerSubheader import org.tasks.filters.NavigationDrawerSubheader
import org.tasks.filters.PlaceFilter import org.tasks.filters.PlaceFilter
@ -46,7 +46,6 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MainActivityViewModel @Inject constructor( class MainActivityViewModel @Inject constructor(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
@ApplicationContext context: Context,
private val defaultFilterProvider: DefaultFilterProvider, private val defaultFilterProvider: DefaultFilterProvider,
private val filterProvider: FilterProvider, private val filterProvider: FilterProvider,
private val taskDao: TaskDao, private val taskDao: TaskDao,
@ -86,10 +85,17 @@ class MainActivityViewModel @Inject constructor(
} }
} }
suspend fun resetFilter() {
setFilter(defaultFilterProvider.getDefaultOpenFilter())
}
fun setFilter( fun setFilter(
filter: Filter, filter: Filter,
task: Task? = null, task: Task? = null,
) { ) {
if (filter == _state.value.filter && task == null) {
return
}
_state.update { _state.update {
it.copy( it.copy(
filter = filter, filter = filter,

@ -6,13 +6,13 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.service.TaskCreator import com.todoroo.astrid.service.TaskCreator
import com.todoroo.astrid.utility.Constants import com.todoroo.astrid.utility.Constants
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.data.TaskAttachment import org.tasks.data.entity.TaskAttachment
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.intents.TaskIntents import org.tasks.intents.TaskIntents
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences

@ -43,10 +43,11 @@ import androidx.lifecycle.lifecycleScope
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.utility.AndroidUtilities.atLeastOreoMR1
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.files.FilesControlSet import com.todoroo.astrid.files.FilesControlSet
import com.todoroo.astrid.repeats.RepeatControlSet import com.todoroo.astrid.repeats.RepeatControlSet
import com.todoroo.astrid.tags.TagsControlSet import com.todoroo.astrid.tags.TagsControlSet
@ -70,10 +71,10 @@ import org.tasks.compose.edit.DueDateRow
import org.tasks.compose.edit.InfoRow import org.tasks.compose.edit.InfoRow
import org.tasks.compose.edit.ListRow import org.tasks.compose.edit.ListRow
import org.tasks.compose.edit.PriorityRow import org.tasks.compose.edit.PriorityRow
import org.tasks.data.Alarm import org.tasks.data.entity.Alarm
import org.tasks.data.Location import org.tasks.data.Location
import org.tasks.data.TagData import org.tasks.data.entity.TagData
import org.tasks.data.UserActivityDao import org.tasks.data.dao.UserActivityDao
import org.tasks.databinding.FragmentTaskEditBinding import org.tasks.databinding.FragmentTaskEditBinding
import org.tasks.databinding.TaskEditCalendarBinding import org.tasks.databinding.TaskEditCalendarBinding
import org.tasks.databinding.TaskEditFilesBinding import org.tasks.databinding.TaskEditFilesBinding
@ -154,6 +155,9 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
discardButtonClick() discardButtonClick()
} }
} }
if (atLeastOreoMR1()) {
activity?.setShowWhenLocked(preferences.showEditScreenWithoutUnlock)
}
binding = FragmentTaskEditBinding.inflate(inflater) binding = FragmentTaskEditBinding.inflate(inflater)
val view: View = binding.root val view: View = binding.root
@ -309,6 +313,13 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
return view return view
} }
override fun onDestroyView() {
super.onDestroyView()
if (atLeastOreoMR1()) {
activity?.setShowWhenLocked(false)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
taskEditEventBus taskEditEventBus
.onEach(this::process) .onEach(this::process)

@ -19,7 +19,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -27,6 +27,7 @@ import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.ui.platform.LocalContext
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
@ -42,15 +43,12 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.room.withTransaction
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.adapter.TaskAdapter import com.todoroo.astrid.adapter.TaskAdapter
import com.todoroo.astrid.adapter.TaskAdapterProvider import com.todoroo.astrid.adapter.TaskAdapterProvider
@ -64,9 +62,7 @@ import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.api.TagFilter import com.todoroo.astrid.api.TagFilter
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.Database
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.repeats.RepeatTaskHelper import com.todoroo.astrid.repeats.RepeatTaskHelper
import com.todoroo.astrid.service.TaskCompleter import com.todoroo.astrid.service.TaskCompleter
import com.todoroo.astrid.service.TaskCreator import com.todoroo.astrid.service.TaskCreator
@ -79,24 +75,33 @@ import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.ShortcutManager import org.tasks.ShortcutManager
import org.tasks.Tasks
import org.tasks.activities.FilterSettingsActivity import org.tasks.activities.FilterSettingsActivity
import org.tasks.activities.GoogleTaskListSettingsActivity import org.tasks.activities.GoogleTaskListSettingsActivity
import org.tasks.activities.PlaceSettingsActivity import org.tasks.activities.PlaceSettingsActivity
import org.tasks.activities.TagSettingsActivity import org.tasks.activities.TagSettingsActivity
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.SubscriptionNagBanner import org.tasks.compose.SubscriptionNagBanner
import org.tasks.compose.collectAsStateLifecycleAware import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.data.CaldavDao
import org.tasks.data.Tag
import org.tasks.data.TagDataDao
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.TagDataDao
import org.tasks.data.db.Database
import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.entity.Tag
import org.tasks.data.entity.Task
import org.tasks.data.listSettingsClass
import org.tasks.data.sql.Join
import org.tasks.data.sql.QueryTemplate
import org.tasks.data.withTransaction
import org.tasks.databinding.FragmentTaskListBinding import org.tasks.databinding.FragmentTaskListBinding
import org.tasks.db.SuspendDbUtils.chunkedMap
import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker
import org.tasks.dialogs.DialogBuilder import org.tasks.dialogs.DialogBuilder
import org.tasks.dialogs.FilterPicker.Companion.newFilterPicker import org.tasks.dialogs.FilterPicker.Companion.newFilterPicker
@ -115,10 +120,12 @@ import org.tasks.preferences.Preferences
import org.tasks.sync.SyncAdapters import org.tasks.sync.SyncAdapters
import org.tasks.tags.TagPickerActivity import org.tasks.tags.TagPickerActivity
import org.tasks.tasklist.DragAndDropRecyclerAdapter import org.tasks.tasklist.DragAndDropRecyclerAdapter
import org.tasks.tasklist.SectionedDataSource
import org.tasks.tasklist.TaskViewHolder import org.tasks.tasklist.TaskViewHolder
import org.tasks.tasklist.ViewHolderFactory import org.tasks.tasklist.ViewHolderFactory
import org.tasks.themes.ColorProvider import org.tasks.themes.ColorProvider
import org.tasks.themes.ThemeColor import org.tasks.themes.ThemeColor
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.ui.TaskEditEvent import org.tasks.ui.TaskEditEvent
import org.tasks.ui.TaskEditEventBus import org.tasks.ui.TaskEditEventBus
import org.tasks.ui.TaskListEvent import org.tasks.ui.TaskListEvent
@ -171,11 +178,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private var mode: ActionMode? = null private var mode: ActionMode? = null
lateinit var themeColor: ThemeColor lateinit var themeColor: ThemeColor
private lateinit var binding: FragmentTaskListBinding private lateinit var binding: FragmentTaskListBinding
private val onBackPressed = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
search.collapseActionView()
}
}
private val sortRequest = private val sortRequest =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
@ -185,7 +187,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
activity?.recreate() activity?.recreate()
} }
if (data.getBooleanExtra(SortSettingsActivity.EXTRA_CHANGED_GROUP, false)) { if (data.getBooleanExtra(SortSettingsActivity.EXTRA_CHANGED_GROUP, false)) {
taskAdapter.clearCollapsed() listViewModel.clearCollapsed()
} }
listViewModel.invalidate() listViewModel.invalidate()
} }
@ -234,7 +236,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
val selectedTaskIds: List<Long> = taskAdapter.getSelected() val selectedTaskIds: List<Long> = taskAdapter.getSelected()
outState.putLongArray(EXTRA_SELECTED_TASK_IDS, selectedTaskIds.toLongArray()) outState.putLongArray(EXTRA_SELECTED_TASK_IDS, selectedTaskIds.toLongArray())
outState.putLongArray(EXTRA_COLLAPSED, taskAdapter.getCollapsed().toLongArray())
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -245,15 +246,21 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
.launchIn(viewLifecycleOwner.lifecycleScope) .launchIn(viewLifecycleOwner.lifecycleScope)
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(requireActivity(), onBackPressed)
}
@OptIn(ExperimentalAnimationApi::class) @OptIn(ExperimentalAnimationApi::class)
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
requireActivity().onBackPressedDispatcher.addCallback(owner = viewLifecycleOwner) {
if (search.isActionViewExpanded) {
search.collapseActionView()
} else {
requireActivity().finish()
if (!preferences.getBoolean(R.string.p_open_last_viewed_list, true)) {
runBlocking {
mainViewModel.resetFilter()
}
}
}
}
binding = FragmentTaskListBinding.inflate(inflater, container, false) binding = FragmentTaskListBinding.inflate(inflater, container, false)
filter = getFilter() filter = getFilter()
val swipeRefreshLayout: SwipeRefreshLayout val swipeRefreshLayout: SwipeRefreshLayout
@ -271,7 +278,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
// set up list adapters // set up list adapters
taskAdapter = taskAdapterProvider.createTaskAdapter(filter) taskAdapter = taskAdapterProvider.createTaskAdapter(filter)
taskAdapter.setCollapsed(savedInstanceState?.getLongArray(EXTRA_COLLAPSED))
listViewModel.setFilter(filter) listViewModel.setFilter(filter)
(recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false (recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.layoutManager = LinearLayoutManager(context)
@ -336,23 +342,42 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
finishActionMode() finishActionMode()
} }
binding.banner.setContent { binding.banner.setContent {
val context = LocalContext.current
val showBanner = listViewModel.state.collectAsStateLifecycleAware().value.begForSubscription val showBanner = listViewModel.state.collectAsStateLifecycleAware().value.begForSubscription
MdcTheme { MdcTheme {
SubscriptionNagBanner( SubscriptionNagBanner(
visible = showBanner, visible = showBanner,
subscribe = { listViewModel.dismissBanner(clickedPurchase = true) }, subscribe = {
dismiss = { listViewModel.dismissBanner(clickedPurchase = false) }, listViewModel.dismissBanner(clickedPurchase = true)
if (Tasks.IS_GOOGLE_PLAY) {
context.startActivity(Intent(context, PurchaseActivity::class.java))
} else {
preferences.lastSubscribeRequest = currentTimeMillis()
context.openUri(R.string.url_donate)
}
},
dismiss = {
listViewModel.dismissBanner(clickedPurchase = false)
},
) )
} }
} }
return binding.root return binding.root
} }
private fun submitList(tasks: List<TaskContainer>) { private fun submitList(tasks: SectionedDataSource) {
if (recyclerAdapter !is DragAndDropRecyclerAdapter) { if (recyclerAdapter !is DragAndDropRecyclerAdapter) {
setAdapter( setAdapter(
DragAndDropRecyclerAdapter( DragAndDropRecyclerAdapter(
taskAdapter, binding.bodyStandard.recyclerView, viewHolderFactory, this, tasks, preferences)) adapter = taskAdapter,
recyclerView = binding.bodyStandard.recyclerView,
viewHolderFactory = viewHolderFactory,
taskList = this,
tasks = tasks,
preferences = preferences,
toggleCollapsed = { listViewModel.toggleCollapsed(it) },
)
)
} else { } else {
recyclerAdapter?.submitList(tasks) recyclerAdapter?.submitList(tasks)
} }
@ -655,7 +680,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
override fun onMenuItemActionExpand(item: MenuItem): Boolean { override fun onMenuItemActionExpand(item: MenuItem): Boolean {
onBackPressed.isEnabled = true
search.setOnQueryTextListener(this) search.setOnQueryTextListener(this)
listViewModel.setSearchQuery("") listViewModel.setSearchQuery("")
if (preferences.isTopAppBar) { if (preferences.isTopAppBar) {
@ -665,7 +689,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
override fun onMenuItemActionCollapse(item: MenuItem): Boolean { override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
onBackPressed.isEnabled = false
search.setOnQueryTextListener(null) search.setOnQueryTextListener(null)
listViewModel.setFilter(filter) listViewModel.setFilter(filter)
listViewModel.setSearchQuery(null) listViewModel.setSearchQuery(null)
@ -880,15 +903,13 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
makeSnackbar(R.string.copy_multiple_tasks_confirmation, duplicates.size.toString())?.show() makeSnackbar(R.string.copy_multiple_tasks_confirmation, duplicates.size.toString())?.show()
} }
fun clearCollapsed() = taskAdapter.clearCollapsed()
override fun onCompletedTask(task: TaskContainer, newState: Boolean) { override fun onCompletedTask(task: TaskContainer, newState: Boolean) {
if (task.isReadOnly) { if (task.isReadOnly) {
return return
} }
lifecycleScope.launch { lifecycleScope.launch {
taskCompleter.setComplete(task.task, newState) taskCompleter.setComplete(task.task, newState)
taskAdapter.onCompletedTask(task, newState) taskAdapter.onCompletedTask(task.uuid, newState)
loadTaskListContent() loadTaskListContent()
} }
} }
@ -996,7 +1017,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
const val ACTION_RELOAD = "action_reload" const val ACTION_RELOAD = "action_reload"
const val ACTION_DELETED = "action_deleted" const val ACTION_DELETED = "action_deleted"
private const val EXTRA_SELECTED_TASK_IDS = "extra_selected_task_ids" private const val EXTRA_SELECTED_TASK_IDS = "extra_selected_task_ids"
private const val EXTRA_COLLAPSED = "extra_collapsed"
private const val VOICE_RECOGNITION_REQUEST_CODE = 1234 private const val VOICE_RECOGNITION_REQUEST_CODE = 1234
private const val EXTRA_FILTER = "extra_filter" private const val EXTRA_FILTER = "extra_filter"
private const val FRAG_TAG_REMOTE_LIST_PICKER = "frag_tag_remote_list_picker" private const val FRAG_TAG_REMOTE_LIST_PICKER = "frag_tag_remote_list_picker"

@ -1,17 +1,17 @@
package com.todoroo.astrid.adapter package com.todoroo.astrid.adapter
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.AstridOrderingFilter import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import com.todoroo.astrid.subtasks.SubtasksFilterUpdater import com.todoroo.astrid.subtasks.SubtasksFilterUpdater
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskListMetadata import org.tasks.data.entity.TaskListMetadata
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import timber.log.Timber import timber.log.Timber
import java.util.Collections import java.util.Collections
import kotlin.math.abs import kotlin.math.abs
@ -66,11 +66,10 @@ class AstridTaskAdapter internal constructor(
override suspend fun onTaskDeleted(task: Task) = updater.onDeleteTask(list, filter, task.uuid) override suspend fun onTaskDeleted(task: Task) = updater.onDeleteTask(list, filter, task.uuid)
override suspend fun onCompletedTask(task: TaskContainer, newState: Boolean) { override suspend fun onCompletedTask(uuid: String, newState: Boolean) {
val itemId = task.uuid val completionDate = if (newState) currentTimeMillis() else 0
val completionDate = if (newState) DateUtilities.now() else 0
if (!newState) { if (!newState) {
val chained = chainedCompletions[itemId] val chained = chainedCompletions[uuid]
if (chained != null) { if (chained != null) {
for (taskId in chained) { for (taskId in chained) {
taskDao.setCompletionDate(taskId, completionDate) taskDao.setCompletionDate(taskId, completionDate)
@ -79,7 +78,7 @@ class AstridTaskAdapter internal constructor(
return return
} }
val chained = ArrayList<String>() val chained = ArrayList<String>()
updater.applyToDescendants(itemId) { node: SubtasksFilterUpdater.Node -> updater.applyToDescendants(uuid) { node: SubtasksFilterUpdater.Node ->
val uuid = node.uuid val uuid = node.uuid
taskDao.setCompletionDate(uuid, completionDate) taskDao.setCompletionDate(uuid, completionDate)
chained.add(node.uuid) chained.add(node.uuid)
@ -90,14 +89,14 @@ class AstridTaskAdapter internal constructor(
var madeChanges = false var madeChanges = false
for (t in tasks) { for (t in tasks) {
if (!isNullOrEmpty(t.recurrence)) { if (!isNullOrEmpty(t.recurrence)) {
updater.moveToParentOf(t.uuid, itemId) updater.moveToParentOf(t.uuid, uuid)
madeChanges = true madeChanges = true
} }
} }
if (madeChanges) { if (madeChanges) {
updater.writeSerialization(list, updater.serializeTree()) updater.writeSerialization(list, updater.serializeTree())
} }
chainedCompletions[itemId] = chained chainedCompletions[uuid] = chained
} }
} }

@ -3,15 +3,15 @@ package com.todoroo.astrid.adapter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
class CaldavManualSortTaskAdapter internal constructor( class CaldavManualSortTaskAdapter internal constructor(
googleTaskDao: GoogleTaskDao, googleTaskDao: GoogleTaskDao,
caldavDao: CaldavDao, caldavDao: CaldavDao,
taskDao: TaskDao, taskDao: TaskDao,
localBroadcastManager: LocalBroadcastManager, localBroadcastManager: LocalBroadcastManager,
taskMover: TaskMover, taskMover: TaskMover,
) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) { ) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) {
override suspend fun moved(from: Int, to: Int, indent: Int) { override suspend fun moved(from: Int, to: Int, indent: Int) {

@ -3,15 +3,15 @@ package com.todoroo.astrid.adapter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
class GoogleTaskManualSortAdapter internal constructor( class GoogleTaskManualSortAdapter internal constructor(
googleTaskDao: GoogleTaskDao, googleTaskDao: GoogleTaskDao,
caldavDao: CaldavDao, caldavDao: CaldavDao,
taskDao: TaskDao, taskDao: TaskDao,
localBroadcastManager: LocalBroadcastManager, localBroadcastManager: LocalBroadcastManager,
taskMover: TaskMover, taskMover: TaskMover,
) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) { ) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) {
override suspend fun moved(from: Int, to: Int, indent: Int) { override suspend fun moved(from: Int, to: Int, indent: Int) {

@ -34,11 +34,11 @@ class NavigationDrawerAdapter @Inject constructor(
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val subheaderClickHandler: SubheaderClickHandler, private val subheaderClickHandler: SubheaderClickHandler,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), ) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
DragAndDropDiffer<FilterListItem, MutableList<FilterListItem>> { DragAndDropDiffer<FilterListItem, ArrayList<FilterListItem>> {
private lateinit var onClick: (FilterListItem?) -> Unit private lateinit var onClick: (FilterListItem?) -> Unit
override val channel = Channel<List<FilterListItem>>(Channel.UNLIMITED) override val channel = Channel<ArrayList<FilterListItem>>(Channel.UNLIMITED)
override val updates: Queue<Pair<MutableList<FilterListItem>, DiffUtil.DiffResult?>> = LinkedList() override val updates: Queue<Pair<ArrayList<FilterListItem>, DiffUtil.DiffResult?>> = LinkedList()
override val scope: CoroutineScope = override val scope: CoroutineScope =
CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher() + Job()) CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher() + Job())
override var items = initializeDiffer(ArrayList()) override var items = initializeDiffer(ArrayList())
@ -84,9 +84,7 @@ class NavigationDrawerAdapter @Inject constructor(
private fun getItem(position: Int) = items[position] private fun getItem(position: Int) = items[position]
override fun transform(list: List<FilterListItem>) = list.toMutableList() override fun diff(last: ArrayList<FilterListItem>, next: ArrayList<FilterListItem>) =
override fun diff(last: MutableList<FilterListItem>, next: MutableList<FilterListItem>) =
DiffUtil.calculateDiff(DiffCallback(last, next)) DiffUtil.calculateDiff(DiffCallback(last, next))
private class DiffCallback(val old: List<FilterListItem>, val new: List<FilterListItem>) : DiffUtil.Callback() { private class DiffCallback(val old: List<FilterListItem>, val new: List<FilterListItem>) : DiffUtil.Callback() {

@ -6,7 +6,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.dialogs.NewFilterDialog import org.tasks.dialogs.NewFilterDialog
import org.tasks.filters.FilterProvider import org.tasks.filters.FilterProvider
import org.tasks.filters.NavigationDrawerSubheader import org.tasks.filters.NavigationDrawerSubheader

@ -11,30 +11,30 @@ import com.todoroo.astrid.core.SortHelper.SORT_LIST
import com.todoroo.astrid.core.SortHelper.SORT_MANUAL import com.todoroo.astrid.core.SortHelper.SORT_MANUAL
import com.todoroo.astrid.core.SortHelper.SORT_START import com.todoroo.astrid.core.SortHelper.SORT_START
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import org.tasks.BuildConfig import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.CaldavTask import org.tasks.data.dao.CaldavDao.Companion.toAppleEpoch
import org.tasks.data.GoogleTaskDao import org.tasks.data.entity.CaldavTask
import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.entity.Task
import org.tasks.data.entity.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.date.DateTimeUtils.toAppleEpoch import org.tasks.data.createDueDate
import org.tasks.data.createHideUntil
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.tasklist.SectionedDataSource.Companion.HEADER_COMPLETED
import org.tasks.time.DateTimeUtils.millisOfDay import org.tasks.time.DateTimeUtils.millisOfDay
open class TaskAdapter( open class TaskAdapter(
private val newTasksOnTop: Boolean, private val newTasksOnTop: Boolean,
private val googleTaskDao: GoogleTaskDao, private val googleTaskDao: GoogleTaskDao,
private val caldavDao: CaldavDao, private val caldavDao: CaldavDao,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val taskMover: TaskMover, private val taskMover: TaskMover,
) { ) {
private val selected = HashSet<Long>() private val selected = HashSet<Long>()
private val collapsed = mutableSetOf(HEADER_COMPLETED)
private lateinit var dataSource: TaskAdapterDataSource private lateinit var dataSource: TaskAdapterDataSource
val count: Int val count: Int
@ -56,15 +56,6 @@ open class TaskAdapter(
fun clearSelections() = selected.clear() fun clearSelections() = selected.clear()
fun getCollapsed() = HashSet(collapsed)
fun setCollapsed(groups: LongArray?) {
clearCollapsed()
groups?.toList()?.let(collapsed::addAll)
}
fun clearCollapsed() = collapsed.retainAll(listOf(HEADER_COMPLETED))
open fun getIndent(task: TaskContainer): Int = task.indent open fun getIndent(task: TaskContainer): Int = task.indent
open fun canMove(source: TaskContainer, from: Int, target: TaskContainer, to: Int): Boolean { open fun canMove(source: TaskContainer, from: Int, target: TaskContainer, to: Int): Boolean {
@ -125,14 +116,6 @@ open class TaskAdapter(
} }
} }
fun toggleCollapsed(group: Long) {
if (collapsed.contains(group)) {
collapsed.remove(group)
} else {
collapsed.add(group)
}
}
open fun supportsAstridSorting(): Boolean = false open fun supportsAstridSorting(): Boolean = false
open suspend fun moved(from: Int, to: Int, indent: Int) { open suspend fun moved(from: Int, to: Int, indent: Int) {
@ -170,7 +153,7 @@ open class TaskAdapter(
fun getItemUuid(position: Int): String = getTask(position).uuid fun getItemUuid(position: Int): String = getTask(position).uuid
open suspend fun onCompletedTask(task: TaskContainer, newState: Boolean) {} open suspend fun onCompletedTask(uuid: String, newState: Boolean) {}
open suspend fun onTaskCreated(uuid: String) {} open suspend fun onTaskCreated(uuid: String) {}
@ -224,7 +207,7 @@ open class TaskAdapter(
task.setDueDateAdjustingHideUntil(when { task.setDueDateAdjustingHideUntil(when {
date == 0L -> 0L date == 0L -> 0L
task.hasDueTime() -> date.toDateTime().withMillisOfDay(original.millisOfDay()).millis task.hasDueTime() -> date.toDateTime().withMillisOfDay(original.millisOfDay()).millis
else -> Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, date) else -> createDueDate(Task.URGENCY_SPECIFIC_DAY, date)
}) })
if (original != task.dueDate) { if (original != task.dueDate) {
taskDao.save(task) taskDao.save(task)

@ -8,7 +8,7 @@ import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.api.TagFilter import com.todoroo.astrid.api.TagFilter
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task.Companion.isUuidEmpty import org.tasks.data.entity.Task.Companion.isUuidEmpty
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import com.todoroo.astrid.subtasks.SubtasksFilterUpdater import com.todoroo.astrid.subtasks.SubtasksFilterUpdater
import com.todoroo.astrid.subtasks.SubtasksHelper import com.todoroo.astrid.subtasks.SubtasksHelper
@ -16,22 +16,22 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.TaskListMetadata import org.tasks.data.entity.TaskListMetadata
import org.tasks.data.TaskListMetadataDao import org.tasks.data.dao.TaskListMetadataDao
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import javax.inject.Inject import javax.inject.Inject
class TaskAdapterProvider @Inject constructor( class TaskAdapterProvider @Inject constructor(
@param:ApplicationContext private val context: Context, @param:ApplicationContext private val context: Context,
private val preferences: Preferences, private val preferences: Preferences,
private val taskListMetadataDao: TaskListMetadataDao, private val taskListMetadataDao: TaskListMetadataDao,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val googleTaskDao: GoogleTaskDao, private val googleTaskDao: GoogleTaskDao,
private val caldavDao: CaldavDao, private val caldavDao: CaldavDao,
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val taskMover: TaskMover, private val taskMover: TaskMover,
) { ) {
fun createTaskAdapter(filter: Filter): TaskAdapter { fun createTaskAdapter(filter: Filter): TaskAdapter {
if (filter is AstridOrderingFilter && preferences.isAstridSort) { if (filter is AstridOrderingFilter && preferences.isAstridSort) {

@ -1,9 +1,8 @@
package com.todoroo.astrid.alarms package com.todoroo.astrid.alarms
import com.todoroo.andlib.utility.DateUtilities import org.tasks.data.entity.Alarm
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Notification
import org.tasks.data.Alarm import org.tasks.data.entity.Task
import org.tasks.jobs.AlarmEntry
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.reminders.Random import org.tasks.reminders.Random
import org.tasks.time.DateTimeUtils.withMillisOfDay import org.tasks.time.DateTimeUtils.withMillisOfDay
@ -19,7 +18,7 @@ class AlarmCalculator(
preferences: Preferences preferences: Preferences
) : this(preferences.isDefaultDueTimeEnabled, Random(), preferences.defaultDueTime) ) : this(preferences.isDefaultDueTimeEnabled, Random(), preferences.defaultDueTime)
fun toAlarmEntry(task: Task, alarm: Alarm): AlarmEntry? { fun toAlarmEntry(task: Task, alarm: Alarm): Notification? {
val trigger = when (alarm.type) { val trigger = when (alarm.type) {
Alarm.TYPE_SNOOZE, Alarm.TYPE_SNOOZE,
Alarm.TYPE_DATE_TIME -> Alarm.TYPE_DATE_TIME ->
@ -51,12 +50,12 @@ class AlarmCalculator(
trigger <= AlarmService.NO_ALARM -> trigger <= AlarmService.NO_ALARM ->
null null
trigger > task.reminderLast || alarm.type == Alarm.TYPE_SNOOZE -> trigger > task.reminderLast || alarm.type == Alarm.TYPE_SNOOZE ->
AlarmEntry(alarm.id, alarm.task, trigger, alarm.type) Notification(taskId = alarm.task, timestamp = trigger, type = alarm.type)
alarm.repeat > 0 -> { alarm.repeat > 0 -> {
val past = (task.reminderLast - trigger) / alarm.interval val past = (task.reminderLast - trigger) / alarm.interval
val next = trigger + (past + 1) * alarm.interval val next = trigger + (past + 1) * alarm.interval
if (past < alarm.repeat && next > task.reminderLast) { if (past < alarm.repeat && next > task.reminderLast) {
AlarmEntry(alarm.id, alarm.task, next, alarm.type) Notification(taskId = alarm.task, timestamp = next, type = alarm.type)
} else { } else {
null null
} }
@ -81,11 +80,7 @@ class AlarmCalculator(
`when` = task.creationDate `when` = task.creationDate
} }
`when` += (reminderPeriod * (0.85f + 0.3f * random.nextFloat())).toLong() `when` += (reminderPeriod * (0.85f + 0.3f * random.nextFloat())).toLong()
if (`when` < DateUtilities.now()) { return Math.max(`when`, task.hideUntil)
`when` =
DateUtilities.now() + ((0.5f + 6 * random.nextFloat()) * DateUtilities.ONE_HOUR).toLong()
}
return `when`
} }
return AlarmService.NO_ALARM return AlarmService.NO_ALARM
} }

@ -5,14 +5,19 @@
*/ */
package com.todoroo.astrid.alarms package com.todoroo.astrid.alarms
import com.todoroo.astrid.data.Task
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.Alarm import org.tasks.data.dao.AlarmDao
import org.tasks.data.Alarm.Companion.TYPE_SNOOZE import org.tasks.data.dao.TaskDao
import org.tasks.data.AlarmDao import org.tasks.data.db.DbUtils
import org.tasks.data.TaskDao import org.tasks.data.entity.Alarm
import org.tasks.jobs.NotificationQueue import org.tasks.data.entity.Alarm.Companion.TYPE_SNOOZE
import org.tasks.data.entity.Notification
import org.tasks.jobs.WorkManager
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences
import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -22,11 +27,12 @@ import javax.inject.Inject
*/ */
class AlarmService @Inject constructor( class AlarmService @Inject constructor(
private val alarmDao: AlarmDao, private val alarmDao: AlarmDao,
private val jobs: NotificationQueue,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val notificationManager: NotificationManager, private val notificationManager: NotificationManager,
private val workManager: WorkManager,
private val alarmCalculator: AlarmCalculator, private val alarmCalculator: AlarmCalculator,
private val preferences: Preferences,
) { ) {
suspend fun getAlarms(taskId: Long): List<Alarm> = alarmDao.getAlarms(taskId) suspend fun getAlarms(taskId: Long): List<Alarm> = alarmDao.getAlarms(taskId)
@ -36,70 +42,80 @@ class AlarmService @Inject constructor(
* @return true if data was changed * @return true if data was changed
*/ */
suspend fun synchronizeAlarms(taskId: Long, alarms: MutableSet<Alarm>): Boolean { suspend fun synchronizeAlarms(taskId: Long, alarms: MutableSet<Alarm>): Boolean {
val task = taskDao.fetch(taskId) ?: return false
var changed = false var changed = false
for (existing in alarmDao.getAlarms(taskId)) { for (existing in alarmDao.getAlarms(taskId)) {
if (!alarms.removeIf { if (!alarms.removeIf { it.same(existing)}) {
it.type == existing.type &&
it.time == existing.time &&
it.repeat == existing.repeat &&
it.interval == existing.interval
}) {
alarmDao.delete(existing) alarmDao.delete(existing)
changed = true changed = true
} }
} }
for (alarm in alarms) { alarmDao.insert(alarms.map { it.copy(task = taskId) })
alarm.task = taskId if (alarms.isNotEmpty()) {
alarmDao.insert(alarm)
changed = true changed = true
} }
if (changed) { if (changed) {
scheduleAlarms(task)
localBroadcastManager.broadcastRefreshList() localBroadcastManager.broadcastRefreshList()
} }
return changed return changed
} }
suspend fun scheduleAllAlarms() {
alarmDao
.getActiveAlarms()
.groupBy { it.task }
.forEach { (taskId, alarms) ->
val task = taskDao.fetch(taskId) ?: return@forEach
scheduleAlarms(task, alarms)
}
}
fun cancelAlarms(taskId: Long) {
jobs.cancelForTask(taskId)
}
suspend fun snooze(time: Long, taskIds: List<Long>) { suspend fun snooze(time: Long, taskIds: List<Long>) {
notificationManager.cancel(taskIds) notificationManager.cancel(taskIds)
alarmDao.getSnoozed(taskIds).let { alarmDao.delete(it) } alarmDao.deleteSnoozed(taskIds)
taskIds.map { Alarm(it, time, TYPE_SNOOZE) }.let { alarmDao.insert(it) } alarmDao.insert(taskIds.map { Alarm(task = it, time = time, type = TYPE_SNOOZE) })
taskDao.touch(taskIds) taskDao.touch(taskIds)
scheduleAlarms(taskIds) workManager.triggerNotifications()
} }
suspend fun scheduleAlarms(taskIds: List<Long>) { suspend fun triggerAlarms(
taskDao.fetch(taskIds).forEach { scheduleAlarms(it) } trigger: suspend (List<Notification>) -> Unit
} ): Long {
if (preferences.isCurrentlyQuietHours) {
/** Schedules alarms for a single task */ return preferences.adjustForQuietHours(currentTimeMillis())
suspend fun scheduleAlarms(task: Task) { }
scheduleAlarms(task, alarmDao.getActiveAlarms(task.id)) val (overdue, _) = getAlarms()
overdue
.sortedBy { it.timestamp }
.also { alarms ->
alarms
.map { it.taskId }
.chunked(DbUtils.MAX_SQLITE_ARGS)
.onEach { alarmDao.deleteSnoozed(it) }
}
.map { it.copy(timestamp = currentTimeMillis()) }
.let { trigger(it) }
val alreadyTriggered = overdue.map { it.taskId }.toSet()
val (moreOverdue, future) = getAlarms()
return moreOverdue
.filterNot { it.type == Alarm.TYPE_RANDOM || alreadyTriggered.contains(it.taskId) }
.plus(future)
.minOfOrNull { it.timestamp }
?: 0
} }
private fun scheduleAlarms(task: Task, alarms: List<Alarm>) { internal suspend fun getAlarms(): Pair<List<Notification>, List<Notification>> {
jobs.cancelForTask(task.id) val start = currentTimeMillis()
val alarmEntries = alarms.mapNotNull { val overdue = ArrayList<Notification>()
alarmCalculator.toAlarmEntry(task, it) val future = ArrayList<Notification>()
} alarmDao.getActiveAlarms()
val next = .groupBy { it.task }
alarmEntries.find { it.type == TYPE_SNOOZE } ?: alarmEntries.minByOrNull { it.time } .forEach { (taskId, alarms) ->
next?.let { jobs.add(it) } val task = taskDao.fetch(taskId) ?: return@forEach
val alarmEntries = alarms.mapNotNull {
alarmCalculator.toAlarmEntry(task, it)
}
val (now, later) = alarmEntries.partition { it.timestamp <= DateTime().startOfMinute().plusMinutes(1).millis }
later
.filter { it.type == TYPE_SNOOZE }
.maxByOrNull { it.timestamp }
?.let { future.add(it) }
?: run {
now.firstOrNull()?.let { overdue.add(it) }
later.minByOrNull { it.timestamp }?.let { future.add(it) }
}
}
Timber.d("took ${currentTimeMillis() - start}ms overdue=${overdue.size} future=${future.size}")
return overdue to future
} }
companion object { companion object {

@ -1,15 +1,15 @@
package com.todoroo.astrid.api package com.todoroo.astrid.api
import com.todoroo.andlib.sql.Criterion.Companion.and import org.tasks.data.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.left import org.tasks.data.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate import org.tasks.data.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.NO_COUNT
import org.tasks.data.entity.Task
import org.tasks.data.dao.TaskDao.TaskCriteria.activeAndVisible
@Parcelize @Parcelize
data class CaldavFilter( data class CaldavFilter(

@ -5,7 +5,7 @@ import org.tasks.themes.CustomIcons
@Parcelize @Parcelize
data class CustomFilter( data class CustomFilter(
val filter: org.tasks.data.Filter, val filter: org.tasks.data.entity.Filter,
) : Filter { ) : Filter {
override val title: String? override val title: String?
get() = filter.title get() = filter.title

@ -0,0 +1,8 @@
package com.todoroo.astrid.api
import kotlinx.parcelize.Parcelize
@Parcelize
class EmptyFilter(override val sql: String? = "WHERE 0", override val title: String? = null) : Filter {
override fun areItemsTheSame(other: FilterListItem): Boolean = false
}

@ -2,6 +2,8 @@ package com.todoroo.astrid.api
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.tasks.data.NO_COUNT
import org.tasks.data.NO_ORDER
interface Filter : FilterListItem, Parcelable { interface Filter : FilterListItem, Parcelable {
val valuesForNewTasks: String? val valuesForNewTasks: String?
@ -28,11 +30,7 @@ interface Filter : FilterListItem, Parcelable {
fun supportsHiddenTasks(): Boolean = true fun supportsHiddenTasks(): Boolean = true
fun supportsSubtasks(): Boolean = true fun supportsSubtasks(): Boolean = true
fun supportsSorting(): Boolean = true fun supportsSorting(): Boolean = true
fun disableHeaders(): Boolean = !supportsSorting()
companion object {
const val NO_ORDER = -1
const val NO_COUNT = -1
}
} }
@Deprecated("Use manual ordering") @Deprecated("Use manual ordering")

@ -1,16 +1,16 @@
package com.todoroo.astrid.api package com.todoroo.astrid.api
import com.todoroo.andlib.sql.Criterion.Companion.and import org.tasks.data.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.left import org.tasks.data.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate import org.tasks.data.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import org.tasks.data.entity.Task
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.GoogleTask import org.tasks.data.GoogleTask
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.NO_COUNT
import org.tasks.data.dao.TaskDao.TaskCriteria.activeAndVisible
@Parcelize @Parcelize
data class GtasksFilter( data class GtasksFilter(

@ -7,6 +7,7 @@
package com.todoroo.astrid.api; package com.todoroo.astrid.api;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils2.currentTimeMillis;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
@ -51,7 +52,7 @@ public final class PermaSql {
/** Replace placeholder strings with actual */ /** Replace placeholder strings with actual */
public static String replacePlaceholdersForQuery(String value) { public static String replacePlaceholdersForQuery(String value) {
if (value.contains(VALUE_NOW)) { if (value.contains(VALUE_NOW)) {
value = value.replace(VALUE_NOW, Long.toString(DateUtilities.now())); value = value.replace(VALUE_NOW, Long.toString(currentTimeMillis()));
} }
if (value.contains(VALUE_EOD) if (value.contains(VALUE_EOD)
|| value.contains(VALUE_EOD_DAY_AFTER) || value.contains(VALUE_EOD_DAY_AFTER)
@ -74,7 +75,7 @@ public final class PermaSql {
public static String replacePlaceholdersForNewTask(String value) { public static String replacePlaceholdersForNewTask(String value) {
if (value.contains(VALUE_NOW)) { if (value.contains(VALUE_NOW)) {
value = value.replace(VALUE_NOW, Long.toString(DateUtilities.now())); value = value.replace(VALUE_NOW, Long.toString(currentTimeMillis()));
} }
if (value.contains(VALUE_EOD) if (value.contains(VALUE_EOD)
|| value.contains(VALUE_EOD_DAY_AFTER) || value.contains(VALUE_EOD_DAY_AFTER)

@ -1,17 +1,17 @@
package com.todoroo.astrid.api package com.todoroo.astrid.api
import com.todoroo.andlib.sql.Criterion import org.tasks.data.sql.Criterion
import com.todoroo.andlib.sql.Join import org.tasks.data.sql.Join
import com.todoroo.andlib.sql.Query import org.tasks.data.sql.Query
import com.todoroo.andlib.sql.QueryTemplate import org.tasks.data.sql.QueryTemplate
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.Geofence import org.tasks.data.entity.Geofence
import org.tasks.data.Place import org.tasks.data.entity.Place
import org.tasks.data.Tag import org.tasks.data.entity.Tag
import org.tasks.data.UserActivity import org.tasks.data.entity.UserActivity
@Parcelize @Parcelize
data class SearchFilter( data class SearchFilter(

@ -1,15 +1,15 @@
package com.todoroo.astrid.api package com.todoroo.astrid.api
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.tasks.data.Tag import org.tasks.data.NO_COUNT
import org.tasks.data.TagData import org.tasks.data.dao.TaskDao.TaskCriteria.activeAndVisible
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
import org.tasks.data.sql.Criterion.Companion.and
import org.tasks.data.sql.Join.Companion.inner
import org.tasks.data.sql.QueryTemplate
@Parcelize @Parcelize
data class TagFilter( data class TagFilter(
@ -35,7 +35,7 @@ data class TagFilter(
get() = tagData.getIcon()!! get() = tagData.getIcon()!!
override val tint: Int override val tint: Int
get() = tagData.getColor()!! get() = tagData.color ?: 0
val uuid: String val uuid: String
get() = tagData.remoteId!! get() = tagData.remoteId!!

@ -7,25 +7,24 @@ package com.todoroo.astrid.core
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import com.todoroo.andlib.sql.Criterion.Companion.and import org.tasks.data.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Criterion.Companion.or import org.tasks.data.sql.Criterion.Companion.or
import com.todoroo.andlib.sql.Join import org.tasks.data.sql.Join
import com.todoroo.andlib.sql.QueryTemplate import org.tasks.data.sql.QueryTemplate
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.data.Task import org.tasks.data.entity.Task
import com.todoroo.astrid.timers.TimerPlugin
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.R import org.tasks.R
import org.tasks.data.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.TaskDao import org.tasks.data.dao.TaskDao
import org.tasks.filters.MyTasksFilter import org.tasks.filters.MyTasksFilter
import org.tasks.filters.NotificationsFilter import org.tasks.filters.NotificationsFilter
import org.tasks.filters.RecentlyModifiedFilter import org.tasks.filters.RecentlyModifiedFilter
import org.tasks.filters.SnoozedFilter import org.tasks.filters.SnoozedFilter
import org.tasks.filters.TimerFilter
import org.tasks.filters.TodayFilter import org.tasks.filters.TodayFilter
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import javax.inject.Inject import javax.inject.Inject
@ -52,7 +51,7 @@ class BuiltInFilterExposer @Inject constructor(
filters.add(getSnoozedFilter(r)) filters.add(getSnoozedFilter(r))
} }
if (taskDao.activeTimers() > 0) { if (taskDao.activeTimers() > 0) {
filters.add(TimerPlugin.createFilter(context)) filters.add(getTimerFilter(r))
} }
if (taskDao.hasNotifications() > 0) { if (taskDao.hasNotifications() > 0) {
filters.add(getNotificationsFilter(context)) filters.add(getNotificationsFilter(context))
@ -144,6 +143,8 @@ class BuiltInFilterExposer @Inject constructor(
fun getSnoozedFilter(r: Resources) = SnoozedFilter(r.getString(R.string.filter_snoozed)) fun getSnoozedFilter(r: Resources) = SnoozedFilter(r.getString(R.string.filter_snoozed))
fun getTimerFilter(r: Resources) = TimerFilter(r.getString(R.string.TFE_workingOn))
fun getNotificationsFilter(context: Context) = NotificationsFilter(context.getString(R.string.notifications)) fun getNotificationsFilter(context: Context) = NotificationsFilter(context.getString(R.string.notifications))
@JvmStatic @JvmStatic

@ -5,7 +5,7 @@ import com.todoroo.astrid.api.BooleanCriterion
import com.todoroo.astrid.api.CustomFilterCriterion import com.todoroo.astrid.api.CustomFilterCriterion
import com.todoroo.astrid.api.MultipleSelectCriterion import com.todoroo.astrid.api.MultipleSelectCriterion
import com.todoroo.astrid.api.TextInputCriterion import com.todoroo.astrid.api.TextInputCriterion
import com.todoroo.astrid.helper.UUIDHelper import org.tasks.data.UUIDHelper
class CriterionInstance { class CriterionInstance {
lateinit var criterion: CustomFilterCriterion lateinit var criterion: CustomFilterCriterion

@ -6,6 +6,7 @@
package com.todoroo.astrid.core; package com.todoroo.astrid.core;
import static org.tasks.data.dao.CaldavDaoKt.APPLE_EPOCH;
import static org.tasks.db.QueryUtils.showCompleted; import static org.tasks.db.QueryUtils.showCompleted;
import static org.tasks.db.QueryUtils.showHidden; import static org.tasks.db.QueryUtils.showHidden;
@ -13,12 +14,12 @@ import android.annotation.SuppressLint;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.todoroo.andlib.sql.Functions; import org.tasks.data.sql.Functions;
import com.todoroo.andlib.sql.Order; import org.tasks.data.sql.Order;
import com.todoroo.andlib.sql.OrderType; import org.tasks.data.sql.OrderType;
import com.todoroo.astrid.data.Task; import org.tasks.data.entity.Task;
import org.tasks.data.CaldavCalendar; import org.tasks.data.entity.CaldavCalendar;
import org.tasks.preferences.QueryPreferences; import org.tasks.preferences.QueryPreferences;
import java.util.Locale; import java.util.Locale;
@ -44,7 +45,6 @@ public class SortHelper {
public static final int SORT_COMPLETED = 10; public static final int SORT_COMPLETED = 10;
public static final int SORT_MANUAL = 11; public static final int SORT_MANUAL = 11;
public static final long APPLE_EPOCH = 978307200000L; // 1/1/2001 GMT
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
public static final String CALDAV_ORDER_COLUMN = public static final String CALDAV_ORDER_COLUMN =
String.format(Locale.US, "IFNULL(tasks.`order`, (tasks.created - %d) / 1000)", APPLE_EPOCH); String.format(Locale.US, "IFNULL(tasks.`order`, (tasks.created - %d) / 1000)", APPLE_EPOCH);

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

@ -5,36 +5,32 @@
*/ */
package com.todoroo.astrid.dao package com.todoroo.astrid.dao
import com.todoroo.astrid.alarms.AlarmService
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.timers.TimerPlugin import com.todoroo.astrid.timers.TimerPlugin
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskDao import org.tasks.data.dao.TaskDao
import org.tasks.data.db.SuspendDbUtils.eachChunk
import org.tasks.data.entity.Task
import org.tasks.data.fetchFiltered
import org.tasks.data.fetchTasks
import org.tasks.data.setCollapsed
import org.tasks.date.DateTimeUtils.isAfterNow import org.tasks.date.DateTimeUtils.isAfterNow
import org.tasks.db.SuspendDbUtils.eachChunk
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.location.GeofenceApi import org.tasks.location.GeofenceApi
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.scheduling.RefreshScheduler
import org.tasks.sync.SyncAdapters import org.tasks.sync.SyncAdapters
import javax.inject.Inject import javax.inject.Inject
class TaskDao @Inject constructor( class TaskDao @Inject constructor(
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val refreshScheduler: RefreshScheduler, private val localBroadcastManager: LocalBroadcastManager,
private val localBroadcastManager: LocalBroadcastManager, private val notificationManager: NotificationManager,
private val notificationManager: NotificationManager, private val geofenceApi: GeofenceApi,
private val geofenceApi: GeofenceApi, private val timerPlugin: TimerPlugin,
private val timerPlugin: TimerPlugin, private val syncAdapters: SyncAdapters,
private val syncAdapters: SyncAdapters, private val workManager: WorkManager,
private val alarmService: AlarmService,
private val workManager: WorkManager,
) { ) {
suspend fun fetch(id: Long): Task? = taskDao.fetch(id) suspend fun fetch(id: Long): Task? = taskDao.fetch(id)
@ -97,17 +93,16 @@ class TaskDao @Inject constructor(
*/ */
suspend fun save(task: Task) = save(task, fetch(task.id)) suspend fun save(task: Task) = save(task, fetch(task.id))
suspend fun saved(original: Task) = suspend fun save(tasks: List<Task>, originals: List<Task>) {
fetch(original.id)?.let { taskDao.updateInternal(tasks)
afterUpdate( tasks.forEach { task -> afterUpdate(task, originals.find { it.id == task.id }) }
it.apply { if (original.isSuppressRefresh()) suppressRefresh() }, }
original
)
}
suspend fun save(task: Task, original: Task?) { suspend fun save(task: Task, original: Task?) {
if (taskDao.update(task, original)) { if (taskDao.update(task, original)) {
afterUpdate(task, original) afterUpdate(task, original)
workManager.triggerNotifications()
workManager.scheduleRefresh()
} }
} }
@ -115,32 +110,24 @@ class TaskDao @Inject constructor(
val completionDateModified = task.completionDate != (original?.completionDate ?: 0) val completionDateModified = task.completionDate != (original?.completionDate ?: 0)
val deletionDateModified = task.deletionDate != (original?.deletionDate ?: 0) val deletionDateModified = task.deletionDate != (original?.deletionDate ?: 0)
val justCompleted = completionDateModified && task.isCompleted val justCompleted = completionDateModified && task.isCompleted
val justDeleted = deletionDateModified && task.isDeleted
if (task.calendarURI?.isNotBlank() == true) { if (task.calendarURI?.isNotBlank() == true) {
workManager.updateCalendar(task) workManager.updateCalendar(task)
} }
coroutineScope { if (justCompleted) {
launch(Dispatchers.Default) { if (task.timerStart > 0) {
if (justCompleted || justDeleted) { timerPlugin.stopTimer(task)
notificationManager.cancel(task.id)
if (task.timerStart > 0) {
timerPlugin.stopTimer(task)
}
}
if (task.dueDate != original?.dueDate && task.dueDate.isAfterNow()) {
notificationManager.cancel(task.id)
}
if (completionDateModified || deletionDateModified) {
geofenceApi.update(task.id)
}
alarmService.scheduleAlarms(task)
refreshScheduler.scheduleRefresh(task)
if (!task.isSuppressRefresh()) {
localBroadcastManager.broadcastRefresh()
}
syncAdapters.sync(task, original)
} }
} }
if (task.dueDate != original?.dueDate && task.dueDate.isAfterNow()) {
notificationManager.cancel(task.id)
}
if (completionDateModified || deletionDateModified) {
geofenceApi.update(task.id)
}
if (!task.isSuppressRefresh()) {
localBroadcastManager.broadcastRefresh()
}
syncAdapters.sync(task, original)
} }
suspend fun createNew(task: Task) = taskDao.createNew(task) suspend fun createNew(task: Task) = taskDao.createNew(task)

@ -1,12 +0,0 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.data;
public class SyncFlags {
public static final String SUPPRESS_SYNC = "suppress_sync";
public static final String FORCE_CALDAV_SYNC = "force_caldav_sync";
}

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

Loading…
Cancel
Save