1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-06 00:02:48 +02:00

Compare commits

..

269 Commits

Author SHA1 Message Date
Valere
192b0ed3fb change way to compute local echo 2020-10-08 14:37:12 +02:00
Valere
c909fc50a2 Perf Tracing server support 2020-10-05 18:44:42 +02:00
ganfra
5cf1175d7f Merge branch 'develop' into feature/performance_tracking 2020-10-01 17:29:22 +02:00
ganfra
7be93e03ba Profiler: clean code 2020-10-01 17:20:51 +02:00
ganfra
98b86e977f Profile send: add one more stage 2020-10-01 16:38:14 +02:00
ganfra
3cb2873f93 Add logs for timeline build in debug 2020-10-01 15:33:57 +02:00
ganfra
bb7cd409e6 Profiler: make it generic so you can use it as you want. 2020-10-01 15:33:48 +02:00
Benoit Marty
28b039fde3 Update CHANGES.md with: Updates Gradle Wrapper from 5.6.4 to 6.6.1. (#2193) 2020-10-01 14:47:35 +02:00
Benoit Marty
762bfc4fcc Merge pull request #2193 from vector-im/gradlew-update-6.6.1
Updates Gradle Wrapper from 5.6.4 to 6.6.1.
2020-10-01 14:44:55 +02:00
gradle-update-robot
39532fc2aa Update Gradle Wrapper from 5.6.4 to 6.6.1.
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2020-10-01 00:53:05 +00:00
ganfra
cef191f1a5 Performance tracking: add more stages in encryption 2020-09-30 19:20:38 +02:00
Valere
b6b73f2361 Merge pull request #2187 from vector-im/feature/forgot_pass_reset_all_4S
Feature/forgot pass reset all 4 s
2020-09-30 18:22:48 +02:00
Benoit Marty
37d6a9b722 ktlint 2020-09-30 18:20:42 +02:00
Benoit Marty
2a2187196f Merge pull request #2190 from vector-im/feature/utd_pagination
Feature/utd pagination
2020-09-30 18:19:00 +02:00
Benoit Marty
881ebff015 Changelog and small improvement 2020-09-30 18:11:30 +02:00
ganfra
f0272fd283 UTD pagination: clean up and add developer settings to enable complete history 2020-09-30 17:56:50 +02:00
ganfra
0a0330a48c UTD : when reaching UTD and invite state event, stop back pagination 2020-09-30 17:54:16 +02:00
Benoit Marty
c09d308df8 Merge pull request #2176 from vector-im/feature/fix_dm_wordings
Wording differentiation for DM "chat" instead of "room"
2020-09-30 17:50:58 +02:00
Benoit Marty
482bb51640 More cleanup 2020-09-30 17:50:36 +02:00
Benoit Marty
ee56307ccc No warning when cancelling the Reset of 4s 2020-09-30 16:47:34 +02:00
Benoit Marty
18950a6b46 Some cleanup 2020-09-30 16:36:17 +02:00
Valere
a4e163885d Better rotation support 2020-09-30 16:17:28 +02:00
Benoit Marty
108a31eca3 Avoid long lines 2020-09-30 15:31:33 +02:00
Benoit Marty
7f26dbe260 Fix compil issue 2020-09-30 15:23:27 +02:00
Benoit Marty
5e2f65ab7a Split long lines 2020-09-30 12:46:28 +02:00
Benoit Marty
c5459cdde4 Use RoomSummaryHolder if available 2020-09-30 12:39:36 +02:00
Benoit Marty
f58829130a More cleanup 2020-09-30 12:30:21 +02:00
Benoit Marty
42a3a64b0d Better way to get direct state of the room (using view state) 2020-09-30 12:26:25 +02:00
Benoit Marty
1986de36a6 Better wording for DM creation (note: this event is hidden in the timeline by default) 2020-09-30 12:12:44 +02:00
Benoit Marty
84202adc5b Add braces to multiline if else 2020-09-30 11:54:23 +02:00
Benoit Marty
07446d2d41 Embedded if 2020-09-30 11:50:07 +02:00
Benoit Marty
14162ecaa0 Add missing case 2020-09-30 11:47:37 +02:00
Benoit Marty
2f054cd438 Fix mistake 2020-09-30 11:45:21 +02:00
Benoit Marty
b5311aa3df Simple code 2020-09-30 11:42:07 +02:00
Valere
3642ca5b4a cleaning 2020-09-30 11:05:06 +02:00
Valere
2908a5d345 Added first ui bootstrap test 2020-09-30 10:13:59 +02:00
Benoit Marty
d225fb7df0 Update Changelog 2020-09-30 10:08:26 +02:00
Onuray Sahin
5d18a7cc82 Lint fixes. 2020-09-30 10:07:20 +02:00
Onuray Sahin
07976988d9 Use RoomSummary to check if the room is direct. 2020-09-30 10:07:20 +02:00
Onuray Sahin
2a96b2c68e Differentiate wordings for the room profile screen. 2020-09-30 10:07:20 +02:00
Onuray Sahin
3a9e6fa97f Changelog added. 2020-09-30 10:07:20 +02:00
Onuray Sahin
24fcb3f58f Differentiate wordings for direct rooms. 2020-09-30 10:07:20 +02:00
Benoit Marty
5beaa93437 Merge pull request #2162 from gradle-update/develop
Add workflow for Update Gradle Wrapper Action.
2020-09-30 09:49:00 +02:00
Benoit Marty
415fb3a432 Merge pull request #2186 from vector-im/feature/bma_intent
Finish what has been started on #1376: use Intent.ACTION_GET_CONTENT …
2020-09-30 09:48:10 +02:00
ganfra
2ab2c5c94b Performance tracker: start implementing send tracking 2020-09-29 21:07:58 +02:00
Valere
1e3bdd6a2e Fix test 2020-09-29 18:00:15 +02:00
Cristian Greco
68907ff6c0 Merge remote-tracking branch 'upstream/develop' into develop 2020-09-29 17:40:55 +02:00
Valere
9f26d015ba Update change log
Fixes #2052
2020-09-29 17:07:56 +02:00
Valere
c20517599e Add option to reset 4S if lost pass/key 2020-09-29 17:05:29 +02:00
Benoit Marty
0bb75eed1f Finish what has been started on #1376: use Intent.ACTION_GET_CONTENT instead of Intent.ACTION_OPEN_DOCUMENT for other pickers 2020-09-29 16:41:55 +02:00
Benoit Marty
2b90f1395f Merge pull request #1376 from dkanada/patch-1
Fix gallery intent for certain apps
2020-09-29 16:29:21 +02:00
Benoit Marty
51f225056c Merge branch 'develop' into patch-1 2020-09-29 16:28:58 +02:00
Benoit Marty
7a494db40b Merge pull request #2167 from vector-im/feature/ui_test
Feature/ui test
2020-09-29 16:13:54 +02:00
Benoit Marty
487bbe42a9 Merge branch 'develop' into feature/ui_test 2020-09-29 16:13:44 +02:00
Benoit Marty
ab74f6c1a8 Merge pull request #2165 from vector-im/feature/timeline_scroll_opti
Feature/timeline scroll opti
2020-09-29 16:11:57 +02:00
Benoit Marty
2def7f3910 PR Review 2020-09-29 16:10:54 +02:00
ganfra
11a4704161 Clean files and update CHANGES 2020-09-29 15:42:48 +02:00
ganfra
8bc0afa75e Timeline: add glide preloading 2020-09-29 15:42:21 +02:00
ganfra
3f5b1083f3 Timeline: add a prefetch backward item 2020-09-29 15:42:21 +02:00
Benoit Marty
435724ffa9 Merge pull request #2182 from vector-im/feature/bma_hs_diag
Create a script to help getting public information form any homeserver
2020-09-29 15:39:15 +02:00
Benoit Marty
b14d22550b PR Review
Cleanup and Add command line to run the UI tests
2020-09-29 15:12:25 +02:00
Valere
f79784bc8c Stabilisation
Hide keyboard before entering text
2020-09-29 12:51:27 +02:00
Valere
6ac401db9b Doc + change log 2020-09-29 12:51:27 +02:00
Valere
bc2c345e21 First automated UI tests 2020-09-29 12:51:27 +02:00
Benoit Marty
577f0e0d9a Create a script to help getting public information form any homeserver 2020-09-29 11:38:19 +02:00
Benoit Marty
a3570a69dd Merge pull request #2181 from vector-im/feature/bma_pin_fix
PIN code: request PIN code if phone has been locked
2020-09-29 10:03:02 +02:00
Benoit Marty
77f06b962d PIN code: request PIN code if phone has been locked 2020-09-28 16:57:36 +02:00
Benoit Marty
d0ec5a13f3 Merge pull request #2166 from vector-im/feature/bma_splash_quick_fix
Fix Splash screen layout, especially on small screens
2020-09-28 13:52:03 +02:00
Benoit Marty
1ed0ef0948 Disable animation on title 2020-09-26 12:02:22 +02:00
Benoit Marty
21f1848499 Fix Splash screen layout, especially on small screens 2020-09-26 11:30:13 +02:00
Benoit Marty
6958d114a9 Version++ 2020-09-25 14:09:25 +02:00
Benoit Marty
1bc42959d0 Merge branch 'release/1.0.8' into develop 2020-09-25 14:08:24 +02:00
Benoit Marty
22cfb64348 Prepare release 1.0.8 2020-09-25 14:08:15 +02:00
Benoit Marty
056405939b Merge pull request #2170 from vector-im/feature/bma_fix_wellknown_redirect
Fix crash when wellknown are malformed
2020-09-25 13:56:41 +02:00
Benoit Marty
2dcaabe4c4 Fix crash when wellknown are malformed, or redirect to some HTML content (reported by rageshakes) 2020-09-25 11:25:27 +02:00
Benoit Marty
bbd86661a4 Merge pull request #2159 from vector-im/feature/bma_pin_settings
Feature/bma pin settings
2020-09-24 17:16:05 +02:00
Benoit Marty
d684c11c65 Iterate on wording 2020-09-24 15:14:08 +02:00
Benoit Marty
a2fb6d5664 Split long line 2020-09-24 12:47:58 +02:00
Benoit Marty
31029e90cc Fix test compilation issue (no detected since Olm was not available) 2020-09-24 12:46:14 +02:00
Benoit Marty
2e95c78f4e Iterate on wording 2020-09-24 12:40:37 +02:00
Cristian Greco
62f620f79b Add workflow for Update Gradle Wrapper Action.
This action keeps Gradle Wrapper up-to-date to the latest release. It
will run every day at midnight (UTC) and create a pull request if a new
Gradle version is available. The updated Wrapper script is validated
(with checksum verification) during the update process, and the Wrapper
is setup so that it will validate the Gradle binary itself on first run
of the new version.

Signed-off-by: Cristian Greco <cristian@regolo.cc>
2020-09-24 11:19:28 +02:00
Benoit Marty
f271968238 Upgrade library PFLockScreen-Android 2020-09-24 08:20:17 +02:00
Benoit Marty
27d3a36c1b Use same animation for all Fragment transaction 2020-09-23 22:12:02 +02:00
Benoit Marty
dfa0308db6 Pin code: user has to enter pin code twice (#2005) 2020-09-23 21:58:21 +02:00
Benoit Marty
299bcc2bc7 Kill the task if PinActivity is cancelled 2020-09-23 21:58:21 +02:00
Benoit Marty
03c66315cb Ganfra's review 2020-09-23 21:58:21 +02:00
Benoit Marty
9f154748ae Avoid using 1, prefer %d (for translation) 2020-09-23 21:58:21 +02:00
Benoit Marty
5eb66c4617 Small fix and avoid using 1, prefer %d (for translation) 2020-09-23 21:58:21 +02:00
Benoit Marty
44b2673848 PIN: Add a setting to hide notification content when PIN code is configured 2020-09-23 21:58:21 +02:00
Benoit Marty
a6cf2b0685 First test if resultCode == Activity.RESULT_OK 2020-09-23 21:57:58 +02:00
Benoit Marty
b8c350488b Avoid code duplication and let the Activity manage the Fragments 2020-09-23 21:57:58 +02:00
Benoit Marty
fb74628aa8 Protect access to pin code setting, and so remove protection to disable the pin code 2020-09-23 21:57:58 +02:00
Benoit Marty
fdedfc954c PIN Code Improvements. Add more settings (#1985)
- enable/disable biometrics
- enbale/disable grace period
2020-09-23 21:57:58 +02:00
Benoit Marty
b8cbafa75d PIN: move setting to a dedicated screen (no other change) 2020-09-23 21:57:36 +02:00
Benoit Marty
9ab053d702 Merge pull request #2145 from vector-im/feature/login_ui
Login ui
2020-09-23 17:00:28 +02:00
Benoit Marty
6e2c733319 Fix lint issue (MissingConstraints). Use a simple FrameLayout 2020-09-23 16:59:42 +02:00
Benoit Marty
ad2191a76e Fix scrolling issue.
Sign in and log in tested ok
2020-09-23 16:59:42 +02:00
Benoit Marty
ecc189aeac Restore logo on login screens
This reverts commit 4cc3e87d64.
2020-09-23 16:59:23 +02:00
Benoit Marty
34d2c3d391 Merge pull request #2152 from vector-im/feature/various_fixes
Redcude requested permission to access file on the phone
2020-09-23 16:57:20 +02:00
Benoit Marty
1699a57850 Merge branch 'develop' into feature/various_fixes 2020-09-23 16:56:13 +02:00
Benoit Marty
ea9b99b38b Merge pull request #2146 from vector-im/feature/outdated_warning
Allow using an outdated homeserver, at user's risk (#1972)
2020-09-23 16:25:23 +02:00
Benoit Marty
8edecf5937 Allow using an outdated homeserver, at user's risk (#1972)
Just warn the user using a non blocking popup
2020-09-23 16:25:01 +02:00
Benoit Marty
cede7b1dc1 Merge pull request #2155 from vector-im/feature/bma_show_timestamp
Add a setting to show timestamp for all messages (#2123)
2020-09-23 16:21:44 +02:00
Benoit Marty
1464f5aa02 Merge branch 'develop' into feature/bma_show_timestamp 2020-09-23 16:21:36 +02:00
Benoit Marty
5ab7ec0bc8 Merge pull request #2154 from vector-im/feature/bma_user_color_cache
Use cache for user color
2020-09-23 16:20:37 +02:00
Benoit Marty
7f85331448 Add a setting to show timestamp for all messages (#2123) 2020-09-22 18:40:37 +02:00
Benoit Marty
7c063972ac Use cache for user color 2020-09-22 17:05:25 +02:00
Benoit Marty
bd72c0ca8d Merge pull request #2153 from vector-im/feature/wl
Feature/wl
2020-09-22 16:04:08 +02:00
Benoit Marty
91fe308113 Import SAS strings 2020-09-22 15:39:56 +02:00
Benoit Marty
78a76a8038 Remove from string resource 2020-09-22 15:38:37 +02:00
Benoit Marty
759974d9a8 Format resource 2020-09-22 15:33:58 +02:00
Benoit Marty
00a3e802fc Merge pull request #2151 from RiotTranslateBot/weblate-element-android-element-app
Update from Weblate
2020-09-22 15:29:43 +02:00
Benoit Marty
160c1b49a1 It work on on Android 10, to check on Android 5 2020-09-22 14:58:47 +02:00
Benoit Marty
5b1737ae46 It work on on Android 10, to check on Android 5 2020-09-22 14:58:47 +02:00
Benoit Marty
fee7701d26 Better management of requested permissions (#2048) 2020-09-22 14:58:47 +02:00
Benoit Marty
1e5122f741 Rename ids 2020-09-22 14:58:25 +02:00
Weblate
d6ba653c21 Merge branch 'origin/develop' into Weblate. 2020-09-22 12:55:48 +00:00
LinAGKar
51b1d1fa87 Translated using Weblate (Swedish)
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/sv/
2020-09-22 12:55:38 +00:00
Marcelo Filho
4249d9d906 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.7% (1848 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/pt_BR/
2020-09-22 12:55:38 +00:00
Kahina Messaoudi
a7e33e9c0a Translated using Weblate (Kabyle)
Currently translated at 99.8% (1868 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-22 12:55:38 +00:00
@a2sc:matrix.org
7a2e70ee2d Translated using Weblate (German)
Currently translated at 99.9% (1870 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/de/
2020-09-22 12:55:37 +00:00
Priit Jõerüüt
416c153d2e Translated using Weblate (Estonian)
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/et/
2020-09-22 12:55:37 +00:00
Benoit Marty
23d911cc2c Merge pull request #2135 from vector-im/feature/safe_workers
Create parent class for all MatrixWorker
2020-09-22 14:35:15 +02:00
Benoit Marty
d868ef0168 Create parent class for all MatrixWorker 2020-09-22 12:06:01 +02:00
Benoit Marty
48b10e6d08 Merge pull request #2139 from enzingerm/callbuttons
Visually disable call buttons/prohibit calling on insufficient permissions
2020-09-21 21:24:36 +02:00
Benoit Marty
4d9bd57336 Fix compilation issue after merge 2020-09-21 21:20:06 +02:00
Benoit Marty
30ab6d5ee7 Merge pull request #2147 from vector-im/feature/quick_fix
Safe call to awaitCallback
2020-09-21 21:18:36 +02:00
Benoit Marty
622faf5471 Merge pull request #2144 from vector-im/feature/fix_filtering_redacted
Feature/fix filtering redacted
2020-09-21 21:17:00 +02:00
ganfra
4d558c5f95 Merge branch 'develop' into feature/fix_filtering_redacted 2020-09-21 20:25:49 +02:00
ganfra
a29ca2ae09 Clean after Benoit's review 2020-09-21 20:25:16 +02:00
Marinus Enzinger
84df86df37 Visually disable call buttons/prohibit calling on insufficient permissions
Signed-off-by: Marinus Enzinger <marinus@enzingerm.de>
2020-09-21 20:20:06 +02:00
Benoit Marty
22e03b14d1 Merge pull request #2138 from enzingerm/widget_permission
Fix widget creation permission check on group calls
2020-09-21 18:32:02 +02:00
Benoit Marty
d1fed08764 Rename tryThis to tryOrNull 2020-09-21 18:26:35 +02:00
Benoit Marty
bca24c0198 Safe call to awaitCallback 2020-09-21 17:51:35 +02:00
Marinus Enzinger
822c47d15f Fix widget creation permission check
Signed-off-by: Marinus Enzinger <marinus@enzingerm.de>
2020-09-21 17:35:44 +02:00
ganfra
03b3b82e0a Update CHANGES 2020-09-21 15:00:53 +02:00
ganfra
8279191339 Fix filtering of redacted events 2020-09-21 14:57:58 +02:00
Benoit Marty
6486b9e5cd Merge pull request #2129 from vector-im/feature/fix_sending_too_long
Feature/fix sending too long
2020-09-21 12:09:13 +02:00
ganfra
4d7b0e3e68 Merge branch 'develop' into feature/fix_sending_too_long 2020-09-21 10:17:52 +02:00
Benoit Marty
66b0e6c68f Merge pull request #2134 from vector-im/feature/fix_regression_verif_dm
Fix / Verification in DM not working
2020-09-21 09:53:26 +02:00
Benoit Marty
69a4312613 Merge branch 'develop' into feature/fix_regression_verif_dm 2020-09-21 09:52:58 +02:00
Benoit Marty
293f867988 Merge pull request #2132 from vector-im/feature/delete_account_data
Add possibility to delete account data and fix bug
2020-09-21 09:52:10 +02:00
LinAGKar
8d2c0dcb48 Translated using Weblate (Swedish)
Currently translated at 100.0% (168 of 168 strings)

Translation: Element Android/Element Android Sdk
Translate-URL: https://translate.riot.im/projects/element-android/element-sdk/sv/
2020-09-21 05:43:08 +00:00
LinAGKar
bdaecc5b2e Translated using Weblate (Swedish)
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/sv/
2020-09-21 05:43:08 +00:00
notramo
fa9ff86ab2 Translated using Weblate (Hungarian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/hu/
2020-09-21 05:43:07 +00:00
Nikita Epifanov
f8e3e33e49 Translated using Weblate (Russian)
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/ru/
2020-09-18 16:43:06 +00:00
Priit Jõerüüt
73778f8669 Translated using Weblate (Estonian)
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/et/
2020-09-18 16:43:05 +00:00
zeritti
cd5a146f1a Translated using Weblate (Czech)
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/cs/
2020-09-18 16:43:05 +00:00
Jeff Huang
3c45b84474 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1872 of 1872 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/zh_Hant/
2020-09-18 16:43:04 +00:00
ganfra
d4682e504c Clean after Benoit's review 2020-09-18 18:34:40 +02:00
ganfra
2717cca267 Room sending: fix again loss of number type 2020-09-18 18:33:49 +02:00
Valere
28d37f0106 Fix / Verification in DM not working
CheckNumberType crashing on timestamp number
2020-09-18 18:03:09 +02:00
Benoit Marty
30b51449ce Manual import of Megolm keys does back up the imported keys 2020-09-18 15:28:06 +02:00
Benoit Marty
4b66ffcd5b Add "show password" in import Megolm keys dialog 2020-09-18 15:00:36 +02:00
Benoit Marty
0e85a3d59f Add an advanced action to reset an account data entry (there is no API to completely delete it) 2020-09-18 12:49:14 +02:00
ganfra
72f8c8ef72 Clean and update CHANGES 2020-09-18 12:25:29 +02:00
ganfra
b227dc3e5c Sending: remove events from Worker params by fetching in db instead 2020-09-17 18:37:33 +02:00
ganfra
144d0e56cc Realm transaction, use semaphore as suggested by Dominaezzz 2020-09-17 18:31:47 +02:00
Benoit Marty
d29d1ead9b Update issue templates 2020-09-17 16:58:43 +02:00
Benoit Marty
216138394f Version++ 2020-09-17 09:32:35 +02:00
Benoit Marty
719189bd90 Merge branch 'release/1.0.7' into develop 2020-09-17 09:31:35 +02:00
Benoit Marty
01af5115a2 Prepare release 1.0.7 2020-09-17 09:31:09 +02:00
Benoit Marty
7761376306 Merge pull request #2120 from vector-im/feature/post_weblate
Feature/post weblate
2020-09-16 22:28:49 +02:00
Benoit Marty
43f34f6330 Fix issue on test compilation 2020-09-16 20:40:05 +02:00
Benoit Marty
a77069297d Remove R.string.copy, use R.string.action_copy (Fix clash with androidx.preferences private resource) 2020-09-16 19:51:43 +02:00
Benoit Marty
36899af36b Strings has been replaced by a plurals 2020-09-16 19:41:24 +02:00
Benoit Marty
bfa4e00fe7 Hyphen can be replaced with dash 2020-09-16 19:38:08 +02:00
Benoit Marty
4c6bb93eaf ellipsis 2020-09-16 19:36:26 +02:00
Benoit Marty
7e62e8e2cd Fix typo 2020-09-16 19:35:39 +02:00
Benoit Marty
762fd02eb7 Format strings.xml 2020-09-16 19:35:27 +02:00
Benoit Marty
825592e443 Merge pull request #2118 from RiotTranslateBot/weblate-element-android-element-app
Update from Weblate
2020-09-16 19:28:17 +02:00
Benoit Marty
4c6234796d Merge pull request #2111 from vector-im/feature/bma_raw_service
Raw service
2020-09-16 17:55:42 +02:00
Weblate
d6dd2a02b5 Merge branch 'origin/develop' into Weblate. 2020-09-16 15:55:23 +00:00
Benoit Marty
b97aed0723 Ganfra's review 2020-09-16 17:00:14 +02:00
Benoit Marty
1614707943 Ganfra's review 2020-09-16 16:58:27 +02:00
Benoit Marty
88b806326e Merge pull request #2117 from vector-im/feature/event_type_filtering
Feature/event type filtering
2020-09-16 16:28:45 +02:00
Benoit Marty
82bf0dcae9 Create a RawService SDK side, to avoid that the SDK manage client needs 2020-09-16 16:17:10 +02:00
ganfra
0ada12e646 Merge branch 'develop' into feature/event_type_filtering 2020-09-16 15:49:16 +02:00
ganfra
a7ae66e0de Clean files and update CHANGES 2020-09-16 15:48:09 +02:00
ganfra
a1f98eb6bf Allow to filter all room member state events in timeline 2020-09-16 15:36:48 +02:00
Benoit Marty
f882986f7d Remove unused member 2020-09-16 13:43:45 +02:00
linsui
9b8bcc4464 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/zh_Hans/
2020-09-16 03:43:01 +00:00
linsui
487a90fba5 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/zh_Hans/
2020-09-16 03:43:00 +00:00
ganfra
abb9a0839a Room summary : change displayable events types 2020-09-15 18:13:09 +02:00
Benoit Marty
7a01be9c0d Merge pull request #2109 from vector-im/feature/fragment_transaction
Feature/fragment transaction
2020-09-15 17:44:54 +02:00
Benoit Marty
23aaa58834 Merge pull request #2101 from vector-im/feature/clean_call
Feature/clean call
2020-09-15 17:43:49 +02:00
ganfra
0077091175 Update CHANGES and clean files 2020-09-15 12:56:37 +02:00
ganfra
624a8ff04c Fragment transaction: allow state loss when needed 2020-09-15 12:36:27 +02:00
Benoit Marty
94a7db26f5 Merge pull request #2104 from ginnyTheCat/develop
Only front camera is mirrored now
2020-09-15 12:33:00 +02:00
Benoit Marty
58edc83e11 Merge branch 'develop' into develop 2020-09-15 12:32:31 +02:00
Valere
653d6c6050 Merge pull request #2108 from vector-im/feature/fix_cannnot_play_video
output stream not closed
2020-09-15 10:33:56 +02:00
Valere
5e39d3c6fb output stream not closed 2020-09-15 10:03:49 +02:00
discapacidad5
ebde029cce Translated using Weblate (Spanish)
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/es/
2020-09-14 23:43:30 +00:00
discapacidad5
dd4391941e Translated using Weblate (Spanish)
Currently translated at 100.0% (168 of 168 strings)

Translation: Element Android/Element Android Sdk
Translate-URL: https://translate.riot.im/projects/element-android/element-sdk/es/
2020-09-14 23:43:30 +00:00
discapacidad5
6e1fc4d84e Translated using Weblate (Spanish)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/es/
2020-09-14 23:43:29 +00:00
AnonymousWebHacker
86603ed1a6 Translated using Weblate (Spanish)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/es/
2020-09-14 23:43:12 +00:00
ziriSut
a246993df3 Translated using Weblate (Kabyle)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-14 23:43:11 +00:00
random
2e88275766 Translated using Weblate (Italian)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/it/
2020-09-14 23:43:09 +00:00
zeritti
eb2166dd4c Translated using Weblate (Czech)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/cs/
2020-09-14 23:43:08 +00:00
Safa Alfulaij
b088c23ea2 Translated using Weblate (Arabic)
Currently translated at 66.1% (111 of 168 strings)

Translation: Element Android/Element Android Sdk
Translate-URL: https://translate.riot.im/projects/element-android/element-sdk/ar/
2020-09-14 23:42:59 +00:00
ginnyTheCat
95eb6926d4 Removed unnecessary semicolon 2020-09-14 21:48:26 +02:00
Constantin Wartenburger
b6454b70a6 Only front camera is mirrored now 2020-09-14 21:09:52 +02:00
ziriSut
a61b9ce1e1 Translated using Weblate (Kabyle)
Currently translated at 97.8% (1826 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 22:39:55 +00:00
Slimane Selyan AMIRI
a265ff3fbe Translated using Weblate (Kabyle)
Currently translated at 97.8% (1826 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 22:39:53 +00:00
Slimane Selyan AMIRI
fc993cb724 Translated using Weblate (Kabyle)
Currently translated at 97.0% (1811 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 13:55:32 +00:00
ziriSut
808eb9a12a Translated using Weblate (Kabyle)
Currently translated at 97.0% (1811 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 13:55:32 +00:00
Kahina Messaoudi
d429800162 Translated using Weblate (Kabyle)
Currently translated at 97.0% (1811 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 11:25:20 +00:00
Kahina Messaoudi
709e9daf40 Translated using Weblate (Kabyle)
Currently translated at 97.0% (1811 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 10:50:18 +00:00
ziriSut
82a1b8b4c0 Translated using Weblate (Kabyle)
Currently translated at 97.0% (1811 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-12 10:50:17 +00:00
ziriSut
fbe6a2ac80 Translated using Weblate (Kabyle)
Currently translated at 62.5% (1167 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-11 23:50:43 +00:00
Kahina Messaoudi
89af162c5a Translated using Weblate (Kabyle)
Currently translated at 62.5% (1167 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-11 23:50:42 +00:00
Benoit Marty
1e0bc51fa2 Remove singletin instance from callback param 2020-09-11 17:09:30 +02:00
Benoit Marty
21a42e310f Show branch name in debug build 2020-09-11 16:49:11 +02:00
Benoit Marty
ba163dbf5c Split into 4 files 2020-09-11 16:27:11 +02:00
Benoit Marty
236f7f8e28 Private and typo 2020-09-11 16:25:30 +02:00
Benoit Marty
ab2a55d417 Rename member for code clarity (we also have an AudioManager) 2020-09-11 16:24:30 +02:00
Benoit Marty
2c96a79a08 Merge pull request #2090 from vector-im/feature/fix_silent_call_ringing
Always use loudspeaker while ringing and a headset is not connected
2020-09-11 16:18:02 +02:00
Benoit Marty
61b91f4015 Merge pull request #2080 from vector-im/feature/polling_work
Feature/polling work
2020-09-11 15:39:49 +02:00
yuuki-san
c274f9b23c Translated using Weblate (Slovak)
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/sk/
2020-09-11 11:43:03 +00:00
yuuki-san
2148411307 Translated using Weblate (Slovak)
Currently translated at 97.6% (164 of 168 strings)

Translation: Element Android/Element Android Sdk
Translate-URL: https://translate.riot.im/projects/element-android/element-sdk/sk/
2020-09-11 11:43:03 +00:00
Kahina Messaoudi
0c34521791 Translated using Weblate (Kabyle)
Currently translated at 56.3% (1052 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-11 11:43:02 +00:00
Slimane Selyan AMIRI
ad969b999d Translated using Weblate (Kabyle)
Currently translated at 56.3% (1052 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/kab/
2020-09-11 11:43:02 +00:00
@a2sc:matrix.org
3bb2034254 Translated using Weblate (German)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/de/
2020-09-11 11:42:53 +00:00
Onuray Sahin
ef16485eac Always use loudspeaker while ringing and a headset is not connected. 2020-09-11 13:30:00 +03:00
Benoit Marty
8abff412d0 Create constant for default value and so fix a bug when setting bad value for delay. 2020-09-11 11:56:25 +02:00
Benoit Marty
b26d379d20 Refresh push pref after diagnostic 2020-09-11 11:49:14 +02:00
Benoit Marty
2ed7be243b Restore TestBatteryOptimization 2020-09-11 11:31:37 +02:00
Benoit Marty
7efc58cb42 Avoid duplication of code and fix issue on OnApplicationUpgradeOrRebootReceiver: background starts even if notification are disabled 2020-09-11 11:13:30 +02:00
Benoit Marty
da09df0e42 format 2020-09-11 10:49:54 +02:00
Benoit Marty
e997610ef2 Add documentation from PR description 2020-09-11 10:47:21 +02:00
Benoit Marty
c2be97741e Restore listener after device rotation 2020-09-11 10:12:56 +02:00
Benoit Marty
db977b8109 Simplify Dialog UI and code 2020-09-11 09:43:10 +02:00
Benoit Marty
6f1875c13a Merge pull request #2089 from vector-im/feature/dendrite_test
Show M_WEAK_PASSWORD error in the password field
2020-09-10 18:10:45 +02:00
Benoit Marty
4dc28a9d62 Reorder settings 2020-09-10 18:08:35 +02:00
Benoit Marty
23f13b092f Plurals 2020-09-10 18:00:06 +02:00
Benoit Marty
f4c4e84ffe Fix false positive from code quality analysis 2020-09-10 17:13:11 +02:00
Valere
8ac3af327b Update change log 2020-09-10 17:11:13 +02:00
Valere
3ff475af7a Avoid scheduling alarm until network is back 2020-09-10 17:10:47 +02:00
Valere
43c24e55ab Restart fdroid sync on application boot 2020-09-10 17:10:47 +02:00
Valere
971b425e17 F-Droid background sync modes 2020-09-10 17:10:47 +02:00
Benoit Marty
b9e8d7187c Merge pull request #2075 from vector-im/feature/strict_mode_tracking
Feature/strict mode tracking
2020-09-10 13:46:44 +02:00
Onuray Sahin
926ff80525 Merge pull request #2086 from vector-im/feature/fix_event_read_elsewhere
Clear the notification when the event is read elsewhere
2020-09-10 13:24:57 +03:00
Onuray Sahin
0cba8f3aa1 Start background sync even if the eventId or roomId is null. 2020-09-10 12:08:05 +03:00
Benoit Marty
11fb2bcdfa ktlint... 2020-09-09 13:34:57 +02:00
Benoit Marty
94e43475e2 Merge pull request #2040 from vector-im/feature/date_formatting
Feature/date formatting
2020-09-09 12:16:18 +02:00
Benoit Marty
01a4905dc8 Changelog 2020-09-09 12:15:27 +02:00
Benoit Marty
8cb7260375 Small changes (PR review) 2020-09-09 12:10:46 +02:00
ganfra
dc04d2848d Default pref: make sure to use app context 2020-09-09 11:52:05 +02:00
ganfra
c2880a5832 Strict mode: add a build entry to enable whenever we want to check 2020-09-09 11:52:05 +02:00
ganfra
979c0832cf Use realmSessionProvider in localEchoRepository 2020-09-09 11:52:05 +02:00
ganfra
fa381cc06d Use a singleton for default shared pref 2020-09-09 11:45:47 +02:00
ganfra
f1d902b9ad Enable strict mode and remove some stuff from the main thread 2020-09-09 11:45:47 +02:00
Benoit Marty
b97d922808 ktlint 2020-09-09 11:33:22 +02:00
ganfra
18dcd6b9b1 Date format: add more comments and fix wrong format kind usage 2020-09-09 11:32:56 +02:00
ganfra
c6178e504f Clean files and update CHANGES 2020-09-09 11:32:56 +02:00
ganfra
0ff28c4f50 Date formatting: try to generalise usage of VectorDateFormatter and get proper formatting for Date + Time 2020-09-09 11:32:16 +02:00
ganfra
73ab32fd92 Start reworking date formatting 2020-09-09 11:32:16 +02:00
LinAGKar
925d4d077f Translated using Weblate (Swedish)
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/sv/
2020-09-09 07:42:55 +00:00
LinAGKar
4581efa4c3 Translated using Weblate (Swedish)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/sv/
2020-09-09 07:42:55 +00:00
Nikita Epifanov
09ccf7cdde Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/ru/
2020-09-09 07:42:54 +00:00
Nikita Epifanov
d1fedcac7c Translated using Weblate (Russian)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/ru/
2020-09-09 07:42:54 +00:00
@a2sc:matrix.org
a7ee451705 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.riot.im/projects/element-android/element-store/de/
2020-09-09 07:42:53 +00:00
@a2sc:matrix.org
11fc0fed75 Translated using Weblate (German)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/de/
2020-09-09 07:42:53 +00:00
Priit Jõerüüt
f932100388 Translated using Weblate (Estonian)
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/et/
2020-09-09 07:42:52 +00:00
Jeff Huang
f0b582fef0 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1867 of 1867 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.riot.im/projects/element-android/element-app/zh_Hant/
2020-09-09 07:42:51 +00:00
Benoit Marty
bf0b6d738a Version++ 2020-09-08 17:49:26 +02:00
Benoit Marty
83a06b9657 Merge branch 'release/1.0.6' into develop 2020-09-08 17:47:15 +02:00
dkanada
3442ebc1c3 improve gallery intent for certain apps 2020-09-04 21:01:49 +09:00
Benoit Marty
8aada10f0d Show M_WEAK_PASSWORD error in the password field 2020-09-03 15:21:21 +02:00
395 changed files with 10490 additions and 2907 deletions

10
.github/ISSUE_TEMPLATE/matrix-sdk.md vendored Normal file
View File

@@ -0,0 +1,10 @@
---
name: Matrix SDK
about: Report issue or ask for a feature regarding the Android Matrix SDK
title: "[SDK] "
labels: matrix-sdk
assignees: ''
---
<!-- This issue template should be used by third party application maintainers, to report a bug or to request a feature on the SDK module of the application Element Android-->

View File

@@ -0,0 +1,18 @@
name: Update Gradle Wrapper
on:
schedule:
- cron: "0 0 * * *"
jobs:
update-gradle-wrapper:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
target-branch: develop

View File

@@ -1,3 +1,83 @@
Changes in Element 1.0.9 (2020-XX-XX)
===================================================
Features ✨:
- Hide encrypted history (before user is invited). Can be shown if wanted in developer settings
Improvements 🙌:
- Wording differentiation for direct rooms (#2176)
- PIN code: request PIN code if phone has been locked
- Small optimisation of scrolling experience in timeline (#2114)
- Allow user to reset cross signing if he has no way to recover (#2052)
Bugfix 🐛:
- Improve support for image/audio/video/file selection with intent changes (#1376)
- Fix Splash layout on small screens
Translations 🗣:
-
SDK API changes ⚠️:
-
Build 🧱:
- Use Update Gradle Wrapper Action
- Updates Gradle Wrapper from 5.6.4 to 6.6.1. (#2193)
Other changes:
- Added registration/verification automated UI tests
- Create a script to help getting public information form any homeserver
Changes in Element 1.0.8 (2020-09-25)
===================================================
Improvements 🙌:
- Add "show password" in import Megolm keys dialog
- Visually disable call buttons in menu and prohibit calling when permissions are insufficient (#2112)
- Better management of requested permissions (#2048)
- Add a setting to show timestamp for all messages (#2123)
- Use cache for user color
- Allow using an outdated homeserver, at user's risk (#1972)
- Restore small logo on login screens and fix scrolling issue on those screens
- PIN Code Improvements: Add more settings: biometrics, grace period, notification content (#1985)
Bugfix 🐛:
- Long message cannot be sent/takes infinite time & blocks other messages (#1397)
- Fix crash when wellknown are malformed, or redirect to some HTML content (reported by rageshakes)
- User Verification in DM not working
- Manual import of Megolm keys does back up the imported keys
- Auto scrolling to the latest message when sending (#2094)
- Fix incorrect permission check when creating widgets (#2137)
- Pin code: user has to enter pin code twice (#2005)
SDK API changes ⚠️:
- Rename `tryThis` to `tryOrNull`
Other changes:
- Add an advanced action to reset an account data entry
Changes in Element 1.0.7 (2020-09-17)
===================================================
Improvements 🙌:
- Handle date formatting properly (show time am/pm if needed, display year when needed)
- Improve F-Droid Notification (#2055)
Bugfix 🐛:
- Clear the notification when the event is read elsewhere (#1822)
- Speakerphone is not used for ringback tone (#1644, #1645)
- Back camera preview is not mirrored anymore (#1776)
- Various report of people that cannot play video (#2107)
- Rooms incorrectly marked as unread (#588)
- Allow users to show/hide room member state events (#1231)
- Fix stuck on loader when launching home
SDK API changes ⚠️:
- Create a new RawService to get plain data from the server.
Other changes:
- Performance: share Realm instance used on UI thread and improve SharedPreferences reading time.
Changes in Element 1.0.6 (2020-09-08)
===================================================

107
docs/ui-tests.md Normal file
View File

@@ -0,0 +1,107 @@
# Automate user interface tests
Element Android ensures that some fundamental flows are properly working by running automated user interface tests.
Ui tests are using the android [Espresso](https://developer.android.com/training/testing/espresso) library.
Tests can be run on a real device, or on a virtual device (such as the emulator in Android Studio).
Currently the test are covering a small set of application flows:
- Registration
- Self verification via emoji
- Self verification via passphrase
## Prerequisites:
Out of the box, the tests use one of the homeservers (located at http://localhost:8080) of the "Demo Federation of Homeservers" (https://github.com/matrix-org/synapse#running-a-demo-federation-of-synapses).
You first need to follow instructions to set up Synapse in development mode at https://github.com/matrix-org/synapse#synapse-development. If you have already installed all dependencies, the steps are:
```shell script
$ git clone https://github.com/matrix-org/synapse.git
$ cd synapse
$ virtualenv -p python3 env
$ source env/bin/activate
(env) $ python -m pip install --no-use-pep517 -e .
```
Every time you want to launch these test homeservers, type:
```shell script
$ virtualenv -p python3 env
$ source env/bin/activate
(env) $ demo/start.sh --no-rate-limit
```
**Emulator/Device set up**
When running the test via android studio on a device, you have to disable system animations in order for the test to work properly.
First, ensure developer mode is enabled:
- To enable developer options, tap the **Build Number** option 7 times. You can find this option in one of the following locations, depending on your Android version:
- Android 9 (API level 28) and higher: **Settings > About Phone > Build Number**
- Android 8.0.0 (API level 26) and Android 8.1.0 (API level 26): **Settings > System > About Phone > Build Number**
- Android 7.1 (API level 25) and lower: **Settings > About Phone > Build Number**
On your device, under **Settings > Developer options**, disable the following 3 settings:
- Window animation scale
- Transition animation scale
- Animator duration scale
## Run the tests
Once Synapse is running, and an emulator is running, you can run the UI tests.
### From the source code
Click on the green arrow in front of each test. Clicking on the arrow in front of the test class, or from the package directory does not always work (Tests not found issue).
### From command line
````shell script
./gradlew vector:connectedGplayDebugAndroidTest
````
To run all the tests from the `vector` module.
In case of trouble, you can try to uninstall the previous installed test APK first with this command:
```shell script
adb uninstall im.vector.app.debug.test
```
## Recipes
We added some specific Espresso IdlingResources, and other utilities for matrix related tests
### Wait for initial sync
```kotlin
// Wait for initial sync and check room list is there
withIdlingResource(initialSyncIdlingResource(uiSession)) {
onView(withId(R.id.roomListContainer))
.check(matches(isDisplayed()))
}
```
### Accessing current activity
```kotlin
val activity = EspressoHelper.getCurrentActivity()!!
val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession()
```
### Interact with other session
It's possible to create a session via the SDK, and then use this session to interact with the one that the emulator is using (to check verifications for example)
```kotlin
@Before
fun initAccount() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = Matrix.getInstance(context)
val userName = "foobar_${System.currentTimeMillis()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
}
```

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Thu Jul 02 12:33:07 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=11657af6356b7587bfb37287b5992e94a9686d5c8a0a1b60b87b9928a2decde5
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

53
gradlew vendored
View File

@@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@@ -138,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

43
gradlew.bat vendored
View File

@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -56,11 +56,19 @@ android {
buildConfigField "boolean", "LOG_PRIVATE_DATA", project.property("vector.debugPrivateData")
// Set to BODY instead of NONE to enable logging
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level." + project.property("vector.httpLogLevel")
buildConfigField "String", "PERF_TRACING_SERVER", project.property("vector.perfTracingServer")
buildConfigField "String", "PERF_TRACING_SERVER_TOKEN", project.property("vector.perfTracingServerToken")
buildConfigField "String", "PERF_TRACING_SERVER_USER", project.property("vector.perfTracingServerUser")
}
release {
buildConfigField "boolean", "LOG_PRIVATE_DATA", "false"
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.NONE"
buildConfigField "String", "PERF_TRACING_SERVER", ""
buildConfigField "String", "PERF_TRACING_SERVER_TOKEN", ""
buildConfigField "String", "PERF_TRACING_SERVER_USER", ""
}
}
@@ -169,6 +177,9 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
debugImplementation 'com.nikitakozlov.pury:pury:1.1.0'
releaseImplementation 'com.nikitakozlov.pury:pury-no-op:1.1.0'
// Bus
implementation 'org.greenrobot:eventbus:3.1.1'

View File

@@ -24,6 +24,7 @@ import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.common.DaggerTestMatrixComponent
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.network.UserAgentHolder
@@ -41,6 +42,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService
@Inject internal lateinit var rawService: RawService
@Inject internal lateinit var userAgentHolder: UserAgentHolder
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
@Inject internal lateinit var olmManager: OlmManager
@@ -61,6 +63,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return authenticationService
}
fun rawService() = rawService
fun legacySessionImporter(): LegacySessionImporter {
return legacySessionImporter
}

View File

@@ -218,7 +218,7 @@ class CommonTestHelper(context: Context) {
.createAccount(userName, password, null, it)
}
// Preform dummy step
// Perform dummy step
val registrationResult = doSync<RegistrationResult> {
matrix.authenticationService
.getRegistrationWizard()

View File

@@ -25,8 +25,16 @@ import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.di.MatrixModule
import org.matrix.android.sdk.internal.di.MatrixScope
import org.matrix.android.sdk.internal.di.NetworkModule
import org.matrix.android.sdk.internal.raw.RawModule
@Component(modules = [TestModule::class, MatrixModule::class, NetworkModule::class, AuthModule::class, TestNetworkModule::class])
@Component(modules = [
TestModule::class,
MatrixModule::class,
NetworkModule::class,
AuthModule::class,
RawModule::class,
TestNetworkModule::class
])
@MatrixScope
internal interface TestMatrixComponent : MatrixComponent {

View File

@@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.crypto
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
@@ -212,7 +212,7 @@ class UnwedgingTest : InstrumentedTest {
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
// we should get back the key and be able to decrypt
val result = tryThis {
val result = tryOrNull {
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
}
Timber.i("## CRYPTO | testUnwedging: decrypt result ${result?.clearEvent}")

View File

@@ -20,7 +20,7 @@ import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
@@ -227,7 +227,7 @@ class WithHeldTests : InstrumentedTest {
mTestHelper.retryPeriodicallyWithLatch(latch) {
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also {
// try to decrypt and force key request
tryThis { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
}
sessionId = timeLineEvent?.root?.content?.toModel<EncryptedEventContent>()?.sessionId
timeLineEvent != null

View File

@@ -21,10 +21,13 @@ import android.content.Context
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Configuration
import androidx.work.WorkManager
import com.nikitakozlov.pury.Pury
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.util.profiling.PerfServerPlugin
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.di.DaggerMatrixComponent
import org.matrix.android.sdk.internal.network.UserAgentHolder
@@ -42,10 +45,12 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService
@Inject internal lateinit var rawService: RawService
@Inject internal lateinit var userAgentHolder: UserAgentHolder
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
@Inject internal lateinit var olmManager: OlmManager
@Inject internal lateinit var sessionManager: SessionManager
@Inject internal lateinit var perfServerPlugin: PerfServerPlugin
init {
Monarchy.init(context)
@@ -54,6 +59,10 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
}
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
if (!BuildConfig.PERF_TRACING_SERVER.isNullOrEmpty()) {
Pury.addPlugin("per-server", perfServerPlugin)
}
}
fun getUserAgent() = userAgentHolder.userAgent
@@ -62,6 +71,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return authenticationService
}
fun rawService() = rawService
fun legacySessionImporter(): LegacySessionImporter {
return legacySessionImporter
}

View File

@@ -17,13 +17,11 @@
package org.matrix.android.sdk.api.auth.data
// Either a list of supported login types, or an error if the homeserver is outdated
sealed class LoginFlowResult {
data class Success(
val supportedLoginTypes: List<String>,
val isLoginAndRegistrationSupported: Boolean,
val homeServerUrl: String
val homeServerUrl: String,
val isOutdatedHomeserver: Boolean
) : LoginFlowResult()
object OutdatedHomeserver : LoginFlowResult()
}

View File

@@ -42,9 +42,6 @@ import org.matrix.android.sdk.api.util.JsonDict
* }
* ]
* }
* "im.vector.riot.jitsi": {
* "preferredDomain": "https://jitsi.riot.im/"
* }
* }
* </pre>
*/
@@ -57,24 +54,5 @@ data class WellKnown(
val identityServer: WellKnownBaseConfig? = null,
@Json(name = "m.integrations")
val integrations: JsonDict? = null,
@Json(name = "im.vector.riot.e2ee")
val e2eAdminSetting: E2EWellKnownConfig? = null,
@Json(name = "im.vector.riot.jitsi")
val jitsiServer: WellKnownPreferredConfig? = null
)
@JsonClass(generateAdapter = true)
data class E2EWellKnownConfig(
@Json(name = "default")
val e2eDefault: Boolean = true
)
@JsonClass(generateAdapter = true)
data class WellKnownPreferredConfig(
@Json(name = "preferredDomain")
val preferredDomain: String? = null
val integrations: JsonDict? = null
)

View File

@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.extensions
import timber.log.Timber
inline fun <A> tryThis(message: String? = null, operation: () -> A): A? {
inline fun <A> tryOrNull(message: String? = null, operation: () -> A): A? {
return try {
operation()
} catch (any: Throwable) {

View File

@@ -17,7 +17,7 @@
package org.matrix.android.sdk.api.failure
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.di.MoshiProvider
import java.io.IOException
@@ -49,7 +49,7 @@ fun Throwable.isInvalidPassword(): Boolean {
*/
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
return if (this is Failure.OtherServerError && this.httpCode == 401) {
tryThis {
tryOrNull {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(this.errorBody)

View File

@@ -132,6 +132,8 @@ data class MatrixError(
const val M_CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
/** (Not documented yet) */
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
/** (Not documented yet) */
const val M_WEAK_PASSWORD = "M_WEAK_PASSWORD"
const val M_TERMS_NOT_SIGNED = "M_TERMS_NOT_SIGNED"

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.raw
sealed class RawCacheStrategy {
// Data is always fetched from the server
object NoCache: RawCacheStrategy()
// Once data is retrieved, it is stored for the provided amount of time.
// In case of error, and if strict is set to false, the cache can be returned if available
data class TtlCache(val validityDurationInMillis: Long, val strict: Boolean): RawCacheStrategy()
// Once retrieved, the data is stored in cache and will be always get from the cache
object InfiniteCache: RawCacheStrategy()
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2020 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.raw
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
/**
* Useful methods to fetch raw data from the server. The access token will not be used to fetched the data
*/
interface RawService {
/**
* Get a URL, either from cache or from the remote server, depending on the cache strategy
*/
fun getUrl(url: String,
rawCacheStrategy: RawCacheStrategy,
matrixCallback: MatrixCallback<String>): Cancelable
/**
* Specific case for the well-known file. Cache validity is 8 hours
*/
fun getWellknown(userId: String, matrixCallback: MatrixCallback<String>): Cancelable
/**
* Clear all the cache data
*/
fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable
}

View File

@@ -110,7 +110,7 @@ interface Session :
* This does not work in doze mode :/
* If battery optimization is on it can work in app standby but that's all :/
*/
fun startAutomaticBackgroundSync(repeatDelay: Long = 30_000L)
fun startAutomaticBackgroundSync(timeOutInSeconds: Long, repeatDelayInSeconds: Long)
fun stopAnyBackgroundSync()

View File

@@ -112,7 +112,8 @@ interface CryptoService {
fun isRoomEncrypted(roomId: String): Boolean
fun encryptEventContent(eventContent: Content,
fun encryptEventContent(eventId: String,
eventContent: Content,
eventType: String,
roomId: String,
callback: MatrixCallback<MXEncryptEventContentResult>)

View File

@@ -33,16 +33,7 @@ data class HomeServerCapabilities(
/**
* Default identity server url, provided in Wellknown
*/
val defaultIdentityServerUrl: String? = null,
/**
* Option to allow homeserver admins to set the default E2EE behaviour back to disabled for DMs / private rooms
* (as it was before) for various environments where this is desired.
*/
val adminE2EByDefault: Boolean = true,
/**
* Preferred Jitsi domain, provided in Wellknown
*/
val preferredJitsiDomain: String? = null
val defaultIdentityServerUrl: String? = null
) {
companion object {
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.send
import org.matrix.android.sdk.api.util.profiling.BaseProfiler
object SendPerformanceProfiler: BaseProfiler<SendPerformanceProfiler.Stages>() {
enum class Stages() {
// LOCAL_ECHO,
ENCRYPT_WORKER,
ENCRYPT_GET_USERS,
ENCRYPT_SET_ROOM_ENCRYPTION,
ENCRYPT_MEGOLM_SHARE_KEYS,
ENCRYPT_MEGOLM_ENCRYPT,
SEND_WORKER,
GET_UP_TO_DATE_ECHO,
SEND_REQUEST,
RECEIVED_IN_SYNC
}
override val name = "SEND_PROFILER"
}

View File

@@ -34,9 +34,10 @@ interface SendService {
* Method to send a generic event asynchronously. If you want to send a state event, please use [StateService] instead.
* @param eventType the type of the event
* @param content the optional body as a json dict.
* @param onBuiltEvent lambda to react to the event creation
* @return a [Cancelable]
*/
fun sendEvent(eventType: String, content: Content?): Cancelable
fun sendEvent(eventType: String, content: Content?, onBuiltEvent: ((Event) -> Unit)? = null): Cancelable
/**
* Method to send a text message asynchronously.
@@ -47,7 +48,12 @@ interface SendService {
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
* @return a [Cancelable]
*/
fun sendTextMessage(text: CharSequence, msgType: String = MessageType.MSGTYPE_TEXT, autoMarkdown: Boolean = false): Cancelable
fun sendTextMessage(
text: CharSequence,
msgType: String = MessageType.MSGTYPE_TEXT,
autoMarkdown: Boolean = false,
onBuiltEvent: ((Event) -> Unit)? = null
): Cancelable
/**
* Method to send a text message with a formatted body.
@@ -56,7 +62,12 @@ interface SendService {
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
* @return a [Cancelable]
*/
fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable
fun sendFormattedTextMessage(
text: String,
formattedText: String,
msgType: String = MessageType.MSGTYPE_TEXT,
onBuiltEvent: ((Event) -> Unit)? = null
): Cancelable
/**
* Method to send a media asynchronously.

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.summary
import org.matrix.android.sdk.api.session.events.model.EventType
object RoomSummaryConstants {
val PREVIEWABLE_TYPES = listOf(
// TODO filter message type (KEY_VERIFICATION_READY, etc.)
EventType.MESSAGE,
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER,
EventType.ENCRYPTED,
EventType.STICKER,
EventType.REACTION
)
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.timeline
data class TimelineEventFilters(
/**
* A flag to filter edit events
*/
val filterEdits: Boolean = false,
/**
* A flag to filter redacted events
*/
val filterRedacted: Boolean = false,
/**
* A flag to filter useless events, such as membership events without any change
*/
val filterUseless: Boolean = false,
/**
* A flag to filter by types. It should be used with [allowedTypes] field
*/
val filterTypes: Boolean = false,
/**
* If [filterTypes] is true, the list of types allowed by the list.
*/
val allowedTypes: List<String> = emptyList()
)

View File

@@ -26,25 +26,9 @@ data class TimelineSettings(
*/
val initialSize: Int,
/**
* A flag to filter edit events
* Filters for timeline event
*/
val filterEdits: Boolean = false,
/**
* A flag to filter redacted events
*/
val filterRedacted: Boolean = false,
/**
* A flag to filter useless events, such as membership events without any change
*/
val filterUseless: Boolean = false,
/**
* A flag to filter by types. It should be used with [allowedTypes] field
*/
val filterTypes: Boolean = false,
/**
* If [filterTypes] is true, the list of types allowed by the list.
*/
val allowedTypes: List<String> = emptyList(),
val filters: TimelineEventFilters = TimelineEventFilters(),
/**
* If true, will build read receipts for each event.
*/

View File

@@ -0,0 +1,98 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import com.nikitakozlov.pury.Pury
private const val ROOT_STAGE = "ROOT_STAGE"
/**
* Base class for profilers, it's implementing Pury start and stop profiling.
* Order of STAGE in enum matters.
*/
abstract class BaseProfiler<STAGE : Enum<STAGE>> {
private val currentProfilers = ArrayList<String>()
abstract val name: String
/**
* Use to start the profiling process. It's necessary to call this method before any other method.
* You should always call stop profiling with the same key param when you want to stop profiling.
*
* @param key unique identifier of the profiling.
*/
fun startProfiling(key: String) {
if (currentProfilers.contains(key)) {
return
}
currentProfilers.add(key)
Pury.startProfiling(profilerName(key), ROOT_STAGE, 0, 1)
}
/**
* Use to stop the profiling process. This will dispatch information to the configured pury plugins.
*
* @param key unique identifier of the profiling.
*/
fun stopProfiling(key: String) {
if (!currentProfilers.contains(key)) {
return
}
Pury.stopProfiling(profilerName(key), ROOT_STAGE, 1)
currentProfilers.remove(key)
}
/**
* Use to profile a block of code. Internally it will call start and stop stage.
*
* @param key unique identifier of the profiling.
* @param stage the current stage to mark
*/
fun profileBlock(key: String, stage: STAGE, block: (() -> Unit)? = null) {
startStage(key, stage)
block?.invoke()
stopStage(key, stage)
}
/**
* Use to add a new stage to profile inside the root profiling.
* You should have called startProfiling with the same key before.
* You should also call stopStage with same key and same stage to mark the end of this stage.
*
* @param key unique identifier of the profiling.
* @param stage the current stage to profile
*/
fun startStage(key: String, stage: STAGE) {
if (!currentProfilers.contains(key)) return
Pury.startProfiling(profilerName(key), stage.name, stage.ordinal + 1, 1)
}
/**
* Use to finish the profiling of a stage.
* You should have called startStage with the same key and same stage before.
*
* @param key unique identifier of the profiling.
* @param stage the current stage to profile
*/
fun stopStage(key: String, stage: STAGE) {
if (!currentProfilers.contains(key)) return
Pury.stopProfiling(profilerName(key), stage.name, 1)
}
private fun profilerName(key: String) = "${name}_$key"
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface PerServerAPI {
@POST("api/result")
fun postReport(@Body profileResult: ProfileReport): Call<Unit>
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
interface ProfileResult {
val name: String?
val depth: Int?
val nestedResults: List<ProfileResult>?
}
@JsonClass(generateAdapter = true)
data class SingleProfileResultRest(
override val name: String? = null,
override val depth: Int? = 0,
override val nestedResults: List<SingleProfileResultRest>? = null,
val startTime: Long? = 0L,
val execTime: Long? = 0L
) : ProfileResult
@JsonClass(generateAdapter = true)
data class ProfileReport(
@Json(name = "user")
val user: String? = null,
@Json(name = "device")
val device: String? = null,
@Json(name = "id")
val id: String? = null,
@Json(name = "rootProfileResults")
val rootProfileResult: SingleProfileResultRest? = null,
@Json(name = "tag")
val tag: String? = null
)

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import dagger.Binds
import dagger.Lazy
import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.internal.di.Unauthenticated
import org.matrix.android.sdk.internal.network.HttpHeaders
import org.matrix.android.sdk.internal.network.RetrofitFactory
@Module
internal abstract class PerfModule {
@Module
companion object {
@Provides
@JvmStatic
fun providesPrefServerAPI(@Unauthenticated okHttpClient: Lazy<OkHttpClient>,
retrofitFactory: RetrofitFactory): PerServerAPI {
val client = okHttpClient.get().newBuilder()
.addInterceptor { chain ->
var request = chain.request()
val token = BuildConfig.PERF_TRACING_SERVER_TOKEN
val newRequestBuilder = request.newBuilder()
newRequestBuilder.header(HttpHeaders.Authorization, "Bearer $token")
request = newRequestBuilder.build()
chain.proceed(request)
}
.hostnameVerifier(OkHostnameVerifier)
.build()
return retrofitFactory.create(client, BuildConfig.PERF_TRACING_SERVER).create(PerServerAPI::class.java)
}
}
@Binds
abstract fun bindPublishTask(task: DefaultPublishPerfTask): PublishPerfTask
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import android.os.Build
import com.nikitakozlov.pury.Plugin
import com.nikitakozlov.pury.profile.ProfilerId
import com.nikitakozlov.pury.result.model.ProfileResult
import com.nikitakozlov.pury.result.model.SingleProfileResult
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import javax.inject.Inject
internal class PerfServerPlugin @Inject constructor(
private val publishPerfTask: PublishPerfTask,
private val taskExecutor: TaskExecutor
) : Plugin {
override fun handleResult(result: ProfileResult?, profilerId: ProfilerId?) {
val report = ProfileReport(
user = BuildConfig.PERF_TRACING_SERVER_USER,
device = Build.DEVICE,
id = profilerId?.profilerName,
rootProfileResult = (result as? SingleProfileResult)?.let { ResultMapper.map(it) },
tag = "original"
)
publishPerfTask.configureWith(PublishPerfTask.Params(report))
.executeBy(taskExecutor)
}
object ResultMapper {
private const val MS_TO_NS = 1000000
fun map(singleProfileResult: SingleProfileResult): SingleProfileResultRest {
return SingleProfileResultRest(
name = singleProfileResult.stageName,
depth = singleProfileResult.depth,
execTime = singleProfileResult.execTime / MS_TO_NS,
startTime = singleProfileResult.startTime / MS_TO_NS,
nestedResults = singleProfileResult.nestedResults.filterIsInstance<SingleProfileResult>().map {
map(it)
}
)
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import com.nikitakozlov.pury.Logger
import com.nikitakozlov.pury.Pury
import timber.log.Timber
object ProfilingConfiguration {
init {
Pury.setLogger(object : Logger {
override fun result(tag: String, message: String) {
Timber.tag(tag)
Timber.v(message)
}
override fun warning(tag: String, message: String) {
Timber.tag(tag)
Timber.w(message)
}
override fun error(tag: String, message: String) {
Timber.tag(tag)
Timber.e(message)
}
})
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.util.profiling
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
internal interface PublishPerfTask : Task<PublishPerfTask.Params, Unit> {
data class Params(
val report: ProfileReport
)
}
internal class DefaultPublishPerfTask @Inject constructor(
private val perfAPI: PerServerAPI
) : PublishPerfTask {
override suspend fun execute(params: PublishPerfTask.Params) {
return executeRequest(null) {
apiCall = perfAPI.postReport(params.report)
}
}
}

View File

@@ -273,16 +273,16 @@ internal class DefaultAuthenticationService @Inject constructor(
}
private suspend fun getLoginFlowResult(authAPI: AuthAPI, versions: Versions, homeServerUrl: String): LoginFlowResult {
return if (versions.isSupportedBySdk()) {
// Get the login flow
val loginFlowResponse = executeRequest<LoginFlowResponse>(null) {
apiCall = authAPI.getLoginFlows()
}
LoginFlowResult.Success(loginFlowResponse.flows.orEmpty().mapNotNull { it.type }, versions.isLoginAndRegistrationSupportedBySdk(), homeServerUrl)
} else {
// Not supported
LoginFlowResult.OutdatedHomeserver
// Get the login flow
val loginFlowResponse = executeRequest<LoginFlowResponse>(null) {
apiCall = authAPI.getLoginFlows()
}
return LoginFlowResult.Success(
loginFlowResponse.flows.orEmpty().mapNotNull { it.type },
versions.isLoginAndRegistrationSupportedBySdk(),
homeServerUrl,
!versions.isSupportedBySdk()
)
}
override fun getRegistrationWizard(): RegistrationWizard {

View File

@@ -18,10 +18,9 @@
package org.matrix.android.sdk.internal.crypto
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event
@@ -32,28 +31,29 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject
internal class CancelGossipRequestWorker(context: Context,
params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<CancelGossipRequestWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
val sessionId: String,
override val sessionId: String,
val requestId: String,
val recipients: Map<String, List<String>>
) {
val recipients: Map<String, List<String>>,
override val lastFailureMessage: String? = null
) : SessionWorkerParams {
companion object {
fun fromRequest(sessionId: String, request: OutgoingGossipingRequest): Params {
return Params(
sessionId = sessionId,
requestId = request.requestId,
recipients = request.recipients
recipients = request.recipients,
lastFailureMessage = null
)
}
}
@@ -64,18 +64,11 @@ internal class CancelGossipRequestWorker(context: Context,
@Inject lateinit var eventBus: EventBus
@Inject lateinit var credentials: Credentials
override suspend fun doWork(): Result {
val errorOutputData = Data.Builder().putBoolean("failed", true).build()
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success(errorOutputData)
val sessionComponent = getSessionComponent(params.sessionId)
?: return Result.success(errorOutputData).also {
// TODO, can this happen? should I update local echo?
Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
}
sessionComponent.inject(this)
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val localId = LocalEcho.createLocalEchoId()
val contentMap = MXUsersDevicesMap<Any>()
val toDeviceContent = ShareRequestCancellation(
@@ -107,13 +100,17 @@ internal class CancelGossipRequestWorker(context: Context,
)
cryptoStore.updateOutgoingGossipingRequestState(params.requestId, OutgoingGossipingRequestState.CANCELLED)
return Result.success()
} catch (exception: Throwable) {
return if (exception.shouldBeRetried()) {
} catch (throwable: Throwable) {
return if (throwable.shouldBeRetried()) {
Result.retry()
} else {
cryptoStore.updateOutgoingGossipingRequestState(params.requestId, OutgoingGossipingRequestState.FAILED_TO_CANCEL)
Result.success(errorOutputData)
buildErrorResult(params, throwable.localizedMessage ?: "error")
}
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -27,10 +27,16 @@ import androidx.lifecycle.LiveData
import com.squareup.moshi.Types
import com.zhuinden.monarchy.Monarchy
import dagger.Lazy
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.listeners.ProgressListener
import org.matrix.android.sdk.api.session.crypto.CryptoService
@@ -48,6 +54,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.send.SendPerformanceProfiler
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MegolmSessionDataImporter
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
@@ -102,12 +109,6 @@ import org.matrix.android.sdk.internal.task.launchToCallback
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.fetchCopied
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.matrix.olm.OlmManager
import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
@@ -345,13 +346,13 @@ internal class DefaultCryptoService @Inject constructor(
// Open the store
cryptoStore.open()
// this can throw if no network
tryThis {
tryOrNull {
uploadDeviceKeys()
}
oneTimeKeysUploader.maybeUploadOneTimeKeys()
// this can throw if no backup
tryThis {
tryOrNull {
keysBackupService.checkAndStartKeysBackup()
}
}
@@ -653,12 +654,14 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Encrypt an event content according to the configuration of the room.
*
* @param eventId the identifier of the event
* @param eventContent the content of the event.
* @param eventType the type of the event.
* @param roomId the room identifier the event will be sent.
* @param callback the asynchronous callback
*/
override fun encryptEventContent(eventContent: Content,
override fun encryptEventContent(eventId: String,
eventContent: Content,
eventType: String,
roomId: String,
callback: MatrixCallback<MXEncryptEventContentResult>) {
@@ -667,7 +670,12 @@ internal class DefaultCryptoService @Inject constructor(
// Timber.v("## CRYPTO | encryptEventContent() : wait after e2e init")
// internalStart(false)
// }
val userIds = getRoomUserIds(roomId)
SendPerformanceProfiler.startStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_GET_USERS)
val userIds = getRoomUserIds(roomId)
SendPerformanceProfiler.stopStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_GET_USERS)
SendPerformanceProfiler.startStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_SET_ROOM_ENCRYPTION)
var alg = roomEncryptorsStore.get(roomId)
if (alg == null) {
val algorithm = getEncryptionAlgorithm(roomId)
@@ -677,12 +685,13 @@ internal class DefaultCryptoService @Inject constructor(
}
}
}
SendPerformanceProfiler.stopStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_SET_ROOM_ENCRYPTION)
val safeAlgorithm = alg
if (safeAlgorithm != null) {
val t0 = System.currentTimeMillis()
Timber.v("## CRYPTO | encryptEventContent() starts")
runCatching {
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
val content = safeAlgorithm.encryptEventContent(eventId, eventContent, eventType, userIds)
Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
}.foldToCallback(callback)
@@ -803,17 +812,17 @@ internal class DefaultCryptoService @Inject constructor(
onRoomKeyEvent(event)
}
EventType.REQUEST_SECRET,
EventType.ROOM_KEY_REQUEST -> {
EventType.ROOM_KEY_REQUEST -> {
// save audit trail
cryptoStore.saveGossipingEvent(event)
// Requests are stacked, and will be handled one by one at the end of the sync (onSyncComplete)
incomingGossipingRequestManager.onGossipingRequestEvent(event)
}
EventType.SEND_SECRET -> {
EventType.SEND_SECRET -> {
cryptoStore.saveGossipingEvent(event)
onSecretSendReceived(event)
}
EventType.ROOM_KEY_WITHHELD -> {
EventType.ROOM_KEY_WITHHELD -> {
onKeyWithHeldReceived(event)
}
else -> {
@@ -892,7 +901,7 @@ internal class DefaultCryptoService @Inject constructor(
*/
private fun handleSDKLevelGossip(secretName: String?, secretValue: String): Boolean {
return when (secretName) {
MASTER_KEY_SSSS_NAME -> {
MASTER_KEY_SSSS_NAME -> {
crossSigningService.onSecretMSKGossip(secretValue)
true
}
@@ -1072,7 +1081,11 @@ internal class DefaultCryptoService @Inject constructor(
throw Exception("Error")
}
megolmSessionDataImporter.handle(importedSessions, true, progressListener)
megolmSessionDataImporter.handle(
megolmSessionsData = importedSessions,
fromBackup = false,
progressListener = progressListener
)
}
}.foldToCallback(callback)
}
@@ -1347,9 +1360,9 @@ internal class DefaultCryptoService @Inject constructor(
override fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? {
return cryptoStore.getWithHeldMegolmSession(roomId, sessionId)
}
/* ==========================================================================================
* For test only
* ========================================================================================== */
/* ==========================================================================================
* For test only
* ========================================================================================== */
@VisibleForTesting
val cryptoStoreForTesting = cryptoStore

View File

@@ -126,7 +126,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
* @param request the request
*/
private fun sendOutgoingGossipingRequest(request: OutgoingGossipingRequest) {
Timber.v("## CRYPTO - GOSSIP sendOutgoingRoomKeyRequest() : Requesting keys $request")
Timber.v("## CRYPTO - GOSSIP sendOutgoingGossipingRequest() : Requesting keys $request")
val params = SendGossipRequestWorker.Params(
sessionId = sessionId,

View File

@@ -18,10 +18,9 @@
package org.matrix.android.sdk.internal.crypto
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event
@@ -34,40 +33,34 @@ import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
internal class SendGossipRequestWorker(context: Context,
params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<SendGossipRequestWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
val sessionId: String,
override val sessionId: String,
val keyShareRequest: OutgoingRoomKeyRequest? = null,
val secretShareRequest: OutgoingSecretRequest? = null
)
val secretShareRequest: OutgoingSecretRequest? = null,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore
@Inject lateinit var eventBus: EventBus
@Inject lateinit var credentials: Credentials
override suspend fun doWork(): Result {
val errorOutputData = Data.Builder().putBoolean("failed", true).build()
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success(errorOutputData)
val sessionComponent = getSessionComponent(params.sessionId)
?: return Result.success(errorOutputData).also {
// TODO, can this happen? should I update local echo?
Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
}
sessionComponent.inject(this)
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val localId = LocalEcho.createLocalEchoId()
val contentMap = MXUsersDevicesMap<Any>()
val eventType: String
@@ -121,7 +114,7 @@ internal class SendGossipRequestWorker(context: Context,
}
}
else -> {
return Result.success(errorOutputData).also {
return buildErrorResult(params, "Unknown empty gossiping request").also {
Timber.e("Unknown empty gossiping request: $params")
}
}
@@ -137,13 +130,17 @@ internal class SendGossipRequestWorker(context: Context,
)
cryptoStore.updateOutgoingGossipingRequestState(requestId, OutgoingGossipingRequestState.SENT)
return Result.success()
} catch (exception: Throwable) {
return if (exception.shouldBeRetried()) {
} catch (throwable: Throwable) {
return if (throwable.shouldBeRetried()) {
Result.retry()
} else {
cryptoStore.updateOutgoingGossipingRequestState(requestId, OutgoingGossipingRequestState.FAILED_TO_SEND)
Result.success(errorOutputData)
buildErrorResult(params, throwable.localizedMessage ?: "error")
}
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -18,10 +18,9 @@
package org.matrix.android.sdk.internal.crypto
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event
@@ -34,22 +33,23 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
internal class SendGossipWorker(context: Context,
params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<SendGossipWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
val sessionId: String,
override val sessionId: String,
val secretValue: String,
val request: IncomingSecretShareRequest
)
val request: IncomingSecretShareRequest,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore
@@ -58,18 +58,11 @@ internal class SendGossipWorker(context: Context,
@Inject lateinit var messageEncrypter: MessageEncrypter
@Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction
override suspend fun doWork(): Result {
val errorOutputData = Data.Builder().putBoolean("failed", true).build()
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success(errorOutputData)
val sessionComponent = getSessionComponent(params.sessionId)
?: return Result.success(errorOutputData).also {
// TODO, can this happen? should I update local echo?
Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
}
sessionComponent.inject(this)
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val localId = LocalEcho.createLocalEchoId()
val eventType: String = EventType.SEND_SECRET
@@ -81,7 +74,7 @@ internal class SendGossipWorker(context: Context,
val requestingUserId = params.request.userId ?: ""
val requestingDeviceId = params.request.deviceId ?: ""
val deviceInfo = cryptoStore.getUserDevice(requestingUserId, requestingDeviceId)
?: return Result.success(errorOutputData).also {
?: return buildErrorResult(params, "Unknown deviceInfo, cannot send message").also {
cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED)
Timber.e("Unknown deviceInfo, cannot send message, sessionId: ${params.request.deviceId}")
}
@@ -94,7 +87,7 @@ internal class SendGossipWorker(context: Context,
if (olmSessionResult?.sessionId == null) {
// no session with this device, probably because there
// were no one-time keys.
return Result.success(errorOutputData).also {
return buildErrorResult(params, "no session with this device").also {
cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED)
Timber.e("no session with this device, probably because there were no one-time keys.")
}
@@ -130,13 +123,17 @@ internal class SendGossipWorker(context: Context,
)
cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.ACCEPTED)
return Result.success()
} catch (exception: Throwable) {
return if (exception.shouldBeRetried()) {
} catch (throwable: Throwable) {
return if (throwable.shouldBeRetried()) {
Result.retry()
} else {
cryptoStore.updateGossipingRequestState(params.request, GossipingRequestState.FAILED_TO_ACCEPTED)
Result.success(errorOutputData)
buildErrorResult(params, throwable.localizedMessage ?: "error")
}
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -39,7 +39,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
* Must be call on the crypto coroutine thread
*
* @param megolmSessionsData megolm sessions.
* @param backUpKeys true to back up them to the homeserver.
* @param fromBackup true if the imported keys are already backed up on the server.
* @param progressListener the progress listener
* @return import room keys result
*/

View File

@@ -33,7 +33,7 @@ internal interface IMXEncrypting {
* @param userIds the room members the event will be sent to.
* @return the encrypted content
*/
suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Content
suspend fun encryptEventContent(eventId: String, eventContent: Content, eventType: String, userIds: List<String>): Content
/**
* In Megolm, each recipient maintains a record of the ratchet value which allows

View File

@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.send.SendPerformanceProfiler
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
@@ -71,17 +72,20 @@ internal class MXMegolmEncryption(
private var sessionRotationPeriodMsgs: Int = 100
private var sessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000
override suspend fun encryptEventContent(eventContent: Content,
override suspend fun encryptEventContent(eventId: String,
eventContent: Content,
eventType: String,
userIds: List<String>): Content {
val ts = System.currentTimeMillis()
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
val devices = getDevicesInRoom(userIds)
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
val outboundSession = ensureOutboundSession(devices.allowedDevices)
val outboundSession = ensureOutboundSession(eventId, devices.allowedDevices)
SendPerformanceProfiler.startStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_MEGOLM_ENCRYPT)
return encryptContent(outboundSession, eventType, eventContent)
.also {
SendPerformanceProfiler.stopStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_MEGOLM_ENCRYPT)
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
}
}
@@ -128,7 +132,7 @@ internal class MXMegolmEncryption(
*
* @param devicesInRoom the devices list
*/
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
private suspend fun ensureOutboundSession(eventId: String, devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
Timber.v("## CRYPTO | ensureOutboundSession start")
var session = outboundSession
if (session == null
@@ -152,7 +156,9 @@ internal class MXMegolmEncryption(
}
}
}
SendPerformanceProfiler.startStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_MEGOLM_SHARE_KEYS)
shareKey(safeSession, shareMap)
SendPerformanceProfiler.stopStage(eventId, SendPerformanceProfiler.Stages.ENCRYPT_MEGOLM_SHARE_KEYS)
return safeSession
}
@@ -307,6 +313,7 @@ internal class MXMegolmEncryption(
// Get canonical Json from
val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson))
val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString)
val map = HashMap<String, Any>()

View File

@@ -38,7 +38,7 @@ internal class MXOlmEncryption(
private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
: IMXEncrypting {
override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Content {
override suspend fun encryptEventContent(eventId: String, eventContent: Content, eventType: String, userIds: List<String>): Content {
// pick the list of recipients based on the membership list.
//
// TODO: there is a race condition here! What if a new user turns up

View File

@@ -20,6 +20,10 @@ package org.matrix.android.sdk.internal.crypto.store.db
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.Sort
import io.realm.kotlin.where
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.session.events.model.Event
@@ -85,10 +89,6 @@ import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.di.CryptoDatabase
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.session.SessionScope
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.Sort
import io.realm.kotlin.where
import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmException
import timber.log.Timber
@@ -372,6 +372,7 @@ internal class RealmCryptoStore @Inject constructor(
}
override fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?) {
Timber.v("## CRYPTO | *** storePrivateKeysInfo ${msk != null}, ${usk != null}, ${ssk != null}")
doRealmTransaction(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignMasterPrivateKey = msk
@@ -407,6 +408,7 @@ internal class RealmCryptoStore @Inject constructor(
}
override fun storeMSKPrivateKey(msk: String?) {
Timber.v("## CRYPTO | *** storeMSKPrivateKey ${msk != null} ")
doRealmTransaction(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignMasterPrivateKey = msk
@@ -415,6 +417,7 @@ internal class RealmCryptoStore @Inject constructor(
}
override fun storeSSKPrivateKey(ssk: String?) {
Timber.v("## CRYPTO | *** storeSSKPrivateKey ${ssk != null} ")
doRealmTransaction(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignSelfSignedPrivateKey = ssk
@@ -423,6 +426,7 @@ internal class RealmCryptoStore @Inject constructor(
}
override fun storeUSKPrivateKey(usk: String?) {
Timber.v("## CRYPTO | *** storeUSKPrivateKey ${usk != null} ")
doRealmTransaction(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignUserPrivateKey = usk
@@ -541,7 +545,7 @@ internal class RealmCryptoStore @Inject constructor(
deviceId = it.deviceId
)
}
monarchy.writeAsync { realm ->
doRealmTransactionAsync(realmConfiguration) { realm ->
realm.where<MyDeviceLastSeenInfoEntity>().findAll().deleteAllFromRealm()
entities.forEach {
realm.insertOrUpdate(it)
@@ -1191,7 +1195,7 @@ internal class RealmCryptoStore @Inject constructor(
.findAll()
.mapNotNull { entity ->
when (entity.type) {
GossipRequestType.KEY -> {
GossipRequestType.KEY -> {
IncomingRoomKeyRequest(
userId = entity.otherUserId,
deviceId = entity.otherDeviceId,

View File

@@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.crypto.store.db
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.crypto.model.MXDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper
@@ -398,7 +398,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
?.addField(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, Long::class.java)
?.setNullable(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, true)
?.transform { deviceInfoEntity ->
tryThis {
tryOrNull {
deviceInfoEntity.setLong(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, now)
}
}

View File

@@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.crypto.store.db.model
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.GossipRequestType
import org.matrix.android.sdk.internal.crypto.GossipingRequestState
import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest
@@ -45,7 +45,7 @@ internal open class IncomingGossipingRequestEntity(@Index var requestId: String?
var type: GossipRequestType
get() {
return tryThis { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
return tryOrNull { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
}
set(value) {
typeStr = value.name
@@ -55,7 +55,7 @@ internal open class IncomingGossipingRequestEntity(@Index var requestId: String?
var requestState: GossipingRequestState
get() {
return tryThis { GossipingRequestState.valueOf(requestStateStr) }
return tryOrNull { GossipingRequestState.valueOf(requestStateStr) }
?: GossipingRequestState.NONE
}
set(value) {

View File

@@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Types
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.GossipRequestType
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequest
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestState
@@ -47,7 +47,7 @@ internal open class OutgoingGossipingRequestEntity(
var type: GossipRequestType
get() {
return tryThis { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
return tryOrNull { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
}
set(value) {
typeStr = value.name
@@ -57,7 +57,7 @@ internal open class OutgoingGossipingRequestEntity(
var requestState: OutgoingGossipingRequestState
get() {
return tryThis { OutgoingGossipingRequestState.valueOf(requestStateStr) }
return tryOrNull { OutgoingGossipingRequestState.valueOf(requestStateStr) }
?: OutgoingGossipingRequestState.UNSENT
}
set(value) {

View File

@@ -54,7 +54,7 @@ internal class DefaultEncryptEventTask @Inject constructor(
// try {
awaitCallback<MXEncryptEventContentResult> {
params.crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, it)
params.crypto.encryptEventContent(localEvent.eventId, localMutableContent, localEvent.type, params.roomId, it)
}.let { result ->
val modifiedContent = HashMap(result.eventContent)
params.keepKeys?.forEach { toKeep ->

View File

@@ -17,17 +17,17 @@
package org.matrix.android.sdk.internal.crypto.verification
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import timber.log.Timber
import javax.inject.Inject
@@ -37,56 +37,56 @@ import javax.inject.Inject
*/
internal class SendVerificationMessageWorker(context: Context,
params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<SendVerificationMessageWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
val event: Event,
val eventId: String,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@Inject
lateinit var sendVerificationMessageTask: SendVerificationMessageTask
@Inject lateinit var sendVerificationMessageTask: SendVerificationMessageTask
@Inject lateinit var localEchoRepository: LocalEchoRepository
@Inject lateinit var cryptoService: CryptoService
@Inject lateinit var cancelSendTracker: CancelSendTracker
@Inject
lateinit var cryptoService: CryptoService
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doWork(): Result {
val errorOutputData = Data.Builder().putBoolean(OUTPUT_KEY_FAILED, true).build()
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success(errorOutputData)
override suspend fun doSafeWork(params: Params): Result {
val localEvent = localEchoRepository.getUpToDateEcho(params.eventId) ?: return buildErrorResult(params, "Event not found")
val localEventId = localEvent.eventId ?: ""
val roomId = localEvent.roomId ?: ""
if (cancelSendTracker.isCancelRequestedFor(localEventId, roomId)) {
return Result.success()
.also {
cancelSendTracker.markCancelled(localEventId, roomId)
Timber.e("## SendEvent: Event sending has been cancelled $localEventId")
}
}
val sessionComponent = getSessionComponent(params.sessionId)
?: return Result.success(errorOutputData).also {
// TODO, can this happen? should I update local echo?
Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
}
sessionComponent.inject(this)
val localId = params.event.eventId ?: ""
return try {
val eventId = sendVerificationMessageTask.execute(
val resultEventId = sendVerificationMessageTask.execute(
SendVerificationMessageTask.Params(
event = params.event,
event = localEvent,
cryptoService = cryptoService
)
)
Result.success(Data.Builder().putString(localId, eventId).build())
} catch (exception: Throwable) {
if (exception.shouldBeRetried()) {
Result.success(Data.Builder().putString(localEventId, resultEventId).build())
} catch (throwable: Throwable) {
if (throwable.shouldBeRetried()) {
Result.retry()
} else {
Result.success(errorOutputData)
buildErrorResult(params, throwable.localizedMessage ?: "error")
}
}
}
companion object {
private const val OUTPUT_KEY_FAILED = "failed"
fun hasFailed(outputData: Data): Boolean {
return outputData.getBoolean(OUTPUT_KEY_FAILED, false)
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -22,6 +22,9 @@ import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.Operation
import androidx.work.WorkInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.R
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
@@ -51,10 +54,8 @@ import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.StringProvider
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.UUID
import java.util.concurrent.TimeUnit
@@ -87,7 +88,7 @@ internal class VerificationTransportRoomMessage(
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
event = event
eventId = event.eventId ?: ""
))
val enqueueInfo = enqueueSendWork(workerParams)
@@ -115,20 +116,30 @@ internal class VerificationTransportRoomMessage(
val observer = object : Observer<List<WorkInfo>> {
override fun onChanged(workInfoList: List<WorkInfo>?) {
workInfoList
?.filter { it.state == WorkInfo.State.SUCCEEDED }
?.firstOrNull { it.id == enqueueInfo.second }
?.let { wInfo ->
if (SendVerificationMessageWorker.hasFailed(wInfo.outputData)) {
Timber.e("## SAS verification [${tx?.transactionId}] failed to send verification message in state : ${tx?.state}")
tx?.cancel(onErrorReason)
} else {
if (onDone != null) {
onDone()
} else {
tx?.state = nextState
when (wInfo.state) {
WorkInfo.State.FAILED -> {
tx?.cancel(onErrorReason)
workLiveData.removeObserver(this)
}
WorkInfo.State.SUCCEEDED -> {
if (SessionSafeCoroutineWorker.hasFailed(wInfo.outputData)) {
Timber.e("## SAS verification [${tx?.transactionId}] failed to send verification message in state : ${tx?.state}")
tx?.cancel(onErrorReason)
} else {
if (onDone != null) {
onDone()
} else {
tx?.state = nextState
}
}
workLiveData.removeObserver(this)
}
else -> {
// nop
}
}
workLiveData.removeObserver(this)
}
}
}
@@ -174,7 +185,7 @@ internal class VerificationTransportRoomMessage(
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
event = event
eventId = event.eventId ?: ""
))
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
@@ -184,7 +195,7 @@ internal class VerificationTransportRoomMessage(
.build()
workManagerProvider.workManager
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
.enqueue()
// I cannot just listen to the given work request, because when used in a uniqueWork,
@@ -199,7 +210,7 @@ internal class VerificationTransportRoomMessage(
?.filter { it.state == WorkInfo.State.SUCCEEDED }
?.firstOrNull { it.id == workRequest.id }
?.let { wInfo ->
if (SendVerificationMessageWorker.hasFailed(wInfo.outputData)) {
if (SessionSafeCoroutineWorker.hasFailed(wInfo.outputData)) {
callback(null, null)
} else {
val eventId = wInfo.outputData.getString(localId)
@@ -229,7 +240,7 @@ internal class VerificationTransportRoomMessage(
)
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
event = event
eventId = event.eventId ?: ""
))
enqueueSendWork(workerParams)
}
@@ -249,7 +260,7 @@ internal class VerificationTransportRoomMessage(
)
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
event = event
eventId = event.eventId ?: ""
))
val enqueueInfo = enqueueSendWork(workerParams)
@@ -280,7 +291,7 @@ internal class VerificationTransportRoomMessage(
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
.build()
return workManagerProvider.workManager
.beginUniqueWork(uniqueQueueName(), ExistingWorkPolicy.APPEND, workRequest)
.beginUniqueWork(uniqueQueueName(), ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
.enqueue() to workRequest.id
}

View File

@@ -16,31 +16,52 @@
*/
package org.matrix.android.sdk.internal.database
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmConfiguration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import timber.log.Timber
suspend fun <T> awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T) = withContext(Dispatchers.Default) {
Realm.getInstance(config).use { bgRealm ->
bgRealm.beginTransaction()
val result: T
try {
val start = System.currentTimeMillis()
result = transaction(bgRealm)
if (isActive) {
bgRealm.commitTransaction()
val end = System.currentTimeMillis()
val time = end - start
Timber.v("Execute transaction in $time millis")
}
} finally {
if (bgRealm.isInTransaction) {
bgRealm.cancelTransaction()
}
}
result
internal fun <T> CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) {
asyncTransaction(monarchy.realmConfiguration, transaction)
}
internal fun <T> CoroutineScope.asyncTransaction(realmConfiguration: RealmConfiguration, transaction: suspend (realm: Realm) -> T) {
launch {
awaitTransaction(realmConfiguration, transaction)
}
}
private val realmSemaphore = Semaphore(1)
suspend fun <T> awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T): T {
return realmSemaphore.withPermit {
withContext(Dispatchers.IO) {
Realm.getInstance(config).use { bgRealm ->
bgRealm.beginTransaction()
val result: T
try {
val start = System.currentTimeMillis()
result = transaction(bgRealm)
if (isActive) {
bgRealm.commitTransaction()
val end = System.currentTimeMillis()
val time = end - start
Timber.v("Execute transaction in $time millis")
}
} finally {
if (bgRealm.isInTransaction) {
bgRealm.cancelTransaction()
}
}
result
}
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database
import io.realm.Realm
import java.io.Closeable
internal class RealmInstanceWrapper(private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable {
override fun close() {
if (closeRealmOnClose) {
realm.close()
}
}
fun <R> withRealm(block: (Realm) -> R): R {
return use {
block(it.realm)
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database
import android.os.Looper
import androidx.annotation.MainThread
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import javax.inject.Inject
import kotlin.concurrent.getOrSet
/**
* This class keeps an instance of realm open in the main thread so you can grab it whenever you want to get a realm
* instance. This does check each time if you are on the main thread or not and returns the appropriate realm instance.
*/
@SessionScope
internal class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy)
: SessionLifecycleObserver {
private val realmThreadLocal = ThreadLocal<Realm>()
/**
* Allow you to execute a block with an opened realm. It automatically closes it if necessary (ie. when not in main thread)
*/
fun <R> withRealm(block: (Realm) -> R): R {
return getRealmWrapper().withRealm(block)
}
@MainThread
override fun onStart() {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
}
@MainThread
override fun onStop() {
realmThreadLocal.get()?.close()
realmThreadLocal.remove()
}
private fun getRealmWrapper(): RealmInstanceWrapper {
val isOnMainThread = isOnMainThread()
val realm = if (isOnMainThread) {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
} else {
Realm.getInstance(monarchy.realmConfiguration)
}
return RealmInstanceWrapper(realm, closeRealmOnClose = !isOnMainThread)
}
private fun isOnMainThread() = Looper.myLooper() == Looper.getMainLooper()
}

View File

@@ -28,7 +28,7 @@ import javax.inject.Inject
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
companion object {
const val SESSION_STORE_SCHEMA_VERSION = 4L
const val SESSION_STORE_SCHEMA_VERSION = 5L
}
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@@ -38,6 +38,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
if (oldVersion <= 1) migrateTo2(realm)
if (oldVersion <= 2) migrateTo3(realm)
if (oldVersion <= 3) migrateTo4(realm)
if (oldVersion <= 4) migrateTo5(realm)
}
private fun migrateTo1(realm: DynamicRealm) {
@@ -54,16 +55,16 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
private fun migrateTo2(realm: DynamicRealm) {
Timber.d("Step 1 -> 2")
realm.schema.get("HomeServerCapabilitiesEntity")
?.addField(HomeServerCapabilitiesEntityFields.ADMIN_E2_E_BY_DEFAULT, Boolean::class.java)
?.addField("adminE2EByDefault", Boolean::class.java)
?.transform { obj ->
obj.setBoolean(HomeServerCapabilitiesEntityFields.ADMIN_E2_E_BY_DEFAULT, true)
obj.setBoolean("adminE2EByDefault", true)
}
}
private fun migrateTo3(realm: DynamicRealm) {
Timber.d("Step 2 -> 3")
realm.schema.get("HomeServerCapabilitiesEntity")
?.addField(HomeServerCapabilitiesEntityFields.PREFERRED_JITSI_DOMAIN, String::class.java)
?.addField("preferredJitsiDomain", String::class.java)
?.transform { obj ->
// Schedule a refresh of the capabilities
obj.setLong(HomeServerCapabilitiesEntityFields.LAST_UPDATED_TIMESTAMP, 0)
@@ -82,4 +83,11 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
.setRequired(PendingThreePidEntityFields.SID, true)
.addField(PendingThreePidEntityFields.SUBMIT_URL, String::class.java)
}
private fun migrateTo5(realm: DynamicRealm) {
Timber.d("Step 4 -> 5")
realm.schema.get("HomeServerCapabilitiesEntity")
?.removeField("adminE2EByDefault")
?.removeField("preferredJitsiDomain")
}
}

View File

@@ -20,21 +20,34 @@ package org.matrix.android.sdk.internal.database.mapper
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
internal object ContentMapper {
private val moshi = MoshiProvider.providesMoshi()
private val adapter = moshi.adapter<Content>(JSON_DICT_PARAMETERIZED_TYPE)
private val castJsonNumberMoshi by lazy {
// We are adding the CheckNumberType as we are serializing/deserializing multiple time in a row
// and we lost typing information doing so.
// We don't want this check to be done on all adapters, so we create a new moshi just for that.
MoshiProvider.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
}
fun map(content: String?): Content? {
fun map(content: String?, castJsonNumbers: Boolean = false): Content? {
return content?.let {
adapter.fromJson(it)
if (castJsonNumbers) {
castJsonNumberMoshi
} else {
moshi
}.adapter<Content>(JSON_DICT_PARAMETERIZED_TYPE).fromJson(it)
}
}
fun map(content: Content?): String? {
return content?.let {
adapter.toJson(it)
moshi.adapter<Content>(JSON_DICT_PARAMETERIZED_TYPE).toJson(it)
}
}
}

View File

@@ -54,7 +54,7 @@ internal object EventMapper {
return eventEntity
}
fun map(eventEntity: EventEntity): Event {
fun map(eventEntity: EventEntity, castJsonNumbers: Boolean = false): Event {
val ud = eventEntity.unsignedData
?.takeIf { it.isNotBlank() }
?.let {
@@ -69,8 +69,8 @@ internal object EventMapper {
return Event(
type = eventEntity.type,
eventId = eventEntity.eventId,
content = ContentMapper.map(eventEntity.content),
prevContent = ContentMapper.map(eventEntity.prevContent),
content = ContentMapper.map(eventEntity.content, castJsonNumbers),
prevContent = ContentMapper.map(eventEntity.prevContent, castJsonNumbers),
originServerTs = eventEntity.originServerTs,
senderId = eventEntity.sender,
stateKey = eventEntity.stateKey,
@@ -96,8 +96,8 @@ internal object EventMapper {
}
}
internal fun EventEntity.asDomain(): Event {
return EventMapper.map(this)
internal fun EventEntity.asDomain(castJsonNumbers: Boolean = false): Event {
return EventMapper.map(this, castJsonNumbers)
}
internal fun Event.toEntity(roomId: String, sendState: SendState, ageLocalTs: Long?): EventEntity {

View File

@@ -30,9 +30,7 @@ internal object HomeServerCapabilitiesMapper {
canChangePassword = entity.canChangePassword,
maxUploadFileSize = entity.maxUploadFileSize,
lastVersionIdentityServerSupported = entity.lastVersionIdentityServerSupported,
defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
adminE2EByDefault = entity.adminE2EByDefault,
preferredJitsiDomain = entity.preferredJitsiDomain
defaultIdentityServerUrl = entity.defaultIdentityServerUrl
)
}
}

View File

@@ -18,26 +18,24 @@
package org.matrix.android.sdk.internal.database.mapper
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.UserEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import io.realm.Realm
import io.realm.RealmConfiguration
import javax.inject.Inject
internal class ReadReceiptsSummaryMapper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {
internal class ReadReceiptsSummaryMapper @Inject constructor(private val realmSessionProvider: RealmSessionProvider) {
fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> {
if (readReceiptsSummaryEntity == null) {
return emptyList()
}
return Realm.getInstance(realmConfiguration).use { realm ->
return realmSessionProvider.withRealm { realm ->
val readReceipts = readReceiptsSummaryEntity.readReceipts
readReceipts
.mapNotNull {
val user = UserEntity.where(realm, it.userId).findFirst()
?: return@mapNotNull null
?: return@mapNotNull null
ReadReceipt(user.asDomain(), it.originServerTs.toLong())
}
}

View File

@@ -17,17 +17,15 @@
package org.matrix.android.sdk.internal.database.model
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import io.realm.RealmObject
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
internal open class HomeServerCapabilitiesEntity(
var canChangePassword: Boolean = true,
var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
var lastVersionIdentityServerSupported: Boolean = false,
var defaultIdentityServerUrl: String? = null,
var adminE2EByDefault: Boolean = true,
var lastUpdatedTimestamp: Long = 0L,
var preferredJitsiDomain: String? = null
var lastUpdatedTimestamp: Long = 0L
) : RealmObject() {
companion object

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
internal open class RawCacheEntity(
@PrimaryKey
var url: String = "",
var data: String = "",
var lastUpdatedTimestamp: Long = 0L
) : RealmObject() {
companion object
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.query
import io.realm.Realm
import io.realm.kotlin.createObject
import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.model.RawCacheEntity
import org.matrix.android.sdk.internal.database.model.RawCacheEntityFields
/**
* Get the current RawCacheEntity, return null if it does not exist
*/
internal fun RawCacheEntity.Companion.get(realm: Realm, url: String): RawCacheEntity? {
return realm.where<RawCacheEntity>()
.equalTo(RawCacheEntityFields.URL, url)
.findFirst()
}
/**
* Get the current RawCacheEntity, create one if it does not exist
*/
internal fun RawCacheEntity.Companion.getOrCreate(realm: Realm, url: String): RawCacheEntity {
return get(realm, url) ?: realm.createObject(url)
}

View File

@@ -17,17 +17,18 @@
package org.matrix.android.sdk.internal.database.query
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import io.realm.Realm
import io.realm.RealmList
import io.realm.RealmQuery
import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<TimelineEventEntity> {
return realm.where<TimelineEventEntity>()
@@ -56,16 +57,10 @@ internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm:
internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
roomId: String,
includesSending: Boolean,
filterContentRelation: Boolean = false,
filterTypes: List<String> = emptyList()): TimelineEventEntity? {
filters: TimelineEventFilters = TimelineEventFilters()): TimelineEventEntity? {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterTypes(filterTypes)
val liveEvents = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes)
if (filterContentRelation) {
liveEvents
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
}
val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterEvents(filters)
val liveEvents = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.where()?.filterEvents(filters)
val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) {
sendingTimelineEvents
} else {
@@ -76,6 +71,24 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
?.findFirst()
}
internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEventFilters): RealmQuery<TimelineEventEntity> {
if (filters.filterTypes) {
`in`(TimelineEventEntityFields.ROOT.TYPE, filters.allowedTypes.toTypedArray())
}
if (filters.filterUseless) {
not()
.equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true)
}
if (filters.filterEdits) {
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
}
if (filters.filterRedacted) {
not().like(TimelineEventEntityFields.ROOT.UNSIGNED_DATA, TimelineEventFilter.Unsigned.REDACTED)
}
return this
}
internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<String>): RealmQuery<TimelineEventEntity> {
return if (filterTypes.isEmpty()) {
this

View File

@@ -23,6 +23,10 @@ import javax.inject.Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class AuthDatabase
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class GlobalDatabase
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SessionDatabase

View File

@@ -22,22 +22,32 @@ import android.content.res.Resources
import com.squareup.moshi.Moshi
import dagger.BindsInstance
import dagger.Component
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.util.profiling.PerfModule
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.auth.AuthModule
import org.matrix.android.sdk.internal.auth.SessionParamsStore
import org.matrix.android.sdk.internal.raw.RawModule
import org.matrix.android.sdk.internal.session.MockHttpInterceptor
import org.matrix.android.sdk.internal.session.TestInterceptor
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import okhttp3.OkHttpClient
import org.matrix.olm.OlmManager
import java.io.File
@Component(modules = [MatrixModule::class, NetworkModule::class, AuthModule::class, NoOpTestModule::class])
@Component(modules = [
MatrixModule::class,
NetworkModule::class,
AuthModule::class,
RawModule::class,
PerfModule::class,
NoOpTestModule::class
])
@MatrixScope
internal interface MatrixComponent {
@@ -53,6 +63,8 @@ internal interface MatrixComponent {
fun authenticationService(): AuthenticationService
fun rawService(): RawService
fun context(): Context
fun matrixConfiguration(): MatrixConfiguration

View File

@@ -21,11 +21,11 @@ import android.content.Context
import android.content.res.Resources
import dagger.Module
import dagger.Provides
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.createBackgroundHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.createBackgroundHandler
import org.matrix.olm.OlmManager
import java.io.File
import java.util.concurrent.Executors

View File

@@ -24,7 +24,7 @@ import okio.BufferedSink
import okio.ForwardingSink
import okio.Sink
import okio.buffer
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import java.io.IOException
internal class ProgressRequestBody(private val delegate: RequestBody,
@@ -40,7 +40,7 @@ internal class ProgressRequestBody(private val delegate: RequestBody,
override fun isDuplex() = delegate.isDuplex()
val length = tryThis { delegate.contentLength() } ?: -1
val length = tryOrNull { delegate.contentLength() } ?: -1
override fun contentLength() = length

View File

@@ -49,7 +49,7 @@ interface CheckNumberType {
val numberAsString = reader.nextString()
val decimal = BigDecimal(numberAsString)
if (decimal.scale() <= 0) {
decimal.intValueExact()
decimal.longValueExact()
} else {
decimal.toDouble()
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.raw
import com.zhuinden.monarchy.Monarchy
import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.model.RawCacheEntity
import org.matrix.android.sdk.internal.di.GlobalDatabase
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import javax.inject.Inject
internal interface CleanRawCacheTask : Task<Unit, Unit>
internal class DefaultCleanRawCacheTask @Inject constructor(
@GlobalDatabase private val monarchy: Monarchy
) : CleanRawCacheTask {
override suspend fun execute(params: Unit) {
monarchy.awaitTransaction { realm ->
realm.where<RawCacheEntity>()
.findAll()
.deleteAllFromRealm()
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.raw
import com.zhuinden.monarchy.Monarchy
import okhttp3.ResponseBody
import org.matrix.android.sdk.api.raw.RawCacheStrategy
import org.matrix.android.sdk.internal.database.model.RawCacheEntity
import org.matrix.android.sdk.internal.database.query.get
import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.di.GlobalDatabase
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import java.util.Date
import javax.inject.Inject
internal interface GetUrlTask : Task<GetUrlTask.Params, String> {
data class Params(
val url: String,
val rawCacheStrategy: RawCacheStrategy
)
}
internal class DefaultGetUrlTask @Inject constructor(
private val rawAPI: RawAPI,
@GlobalDatabase private val monarchy: Monarchy
) : GetUrlTask {
override suspend fun execute(params: GetUrlTask.Params): String {
return when (params.rawCacheStrategy) {
RawCacheStrategy.NoCache -> doRequest(params.url)
is RawCacheStrategy.TtlCache -> doRequestWithCache(
params.url,
params.rawCacheStrategy.validityDurationInMillis,
params.rawCacheStrategy.strict
)
RawCacheStrategy.InfiniteCache -> doRequestWithCache(
params.url,
Long.MAX_VALUE,
true
)
}
}
private suspend fun doRequest(url: String): String {
return executeRequest<ResponseBody>(null) {
apiCall = rawAPI.getUrl(url)
}
.string()
}
private suspend fun doRequestWithCache(url: String, validityDurationInMillis: Long, strict: Boolean): String {
// Get data from cache
var dataFromCache: String? = null
var isCacheValid = false
monarchy.doWithRealm { realm ->
val entity = RawCacheEntity.get(realm, url)
dataFromCache = entity?.data
isCacheValid = entity != null && Date().time < entity.lastUpdatedTimestamp + validityDurationInMillis
}
if (dataFromCache != null && isCacheValid) {
return dataFromCache as String
}
// No cache or outdated cache
val data = try {
doRequest(url)
} catch (throwable: Throwable) {
// In case of error, we can return value from cache even if outdated
return dataFromCache
?.takeIf { !strict }
?: throw throwable
}
// Store cache
monarchy.awaitTransaction { realm ->
val rawCacheEntity = RawCacheEntity.getOrCreate(realm, url)
rawCacheEntity.data = data
rawCacheEntity.lastUpdatedTimestamp = Date().time
}
return data
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.raw
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.raw.RawCacheStrategy
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import java.util.concurrent.TimeUnit
import javax.inject.Inject
internal class DefaultRawService @Inject constructor(
private val taskExecutor: TaskExecutor,
private val getUrlTask: GetUrlTask,
private val cleanRawCacheTask: CleanRawCacheTask
) : RawService {
override fun getUrl(url: String,
rawCacheStrategy: RawCacheStrategy,
matrixCallback: MatrixCallback<String>): Cancelable {
return getUrlTask
.configureWith(GetUrlTask.Params(url, rawCacheStrategy)) {
callback = matrixCallback
}
.executeBy(taskExecutor)
}
override fun getWellknown(userId: String,
matrixCallback: MatrixCallback<String>): Cancelable {
val homeServerDomain = userId.substringAfter(":")
return getUrl(
"https://$homeServerDomain/.well-known/matrix/client",
RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false),
matrixCallback
)
}
override fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable {
return cleanRawCacheTask
.configureWith(Unit) {
callback = matrixCallback
}
.executeBy(taskExecutor)
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +15,16 @@
* limitations under the License.
*/
package im.vector.app.features.home
package org.matrix.android.sdk.internal.raw
import androidx.annotation.ColorRes
import im.vector.app.R
import io.realm.annotations.RealmModule
import org.matrix.android.sdk.internal.database.model.RawCacheEntity
@ColorRes
fun getColorFromRoomId(roomId: String?): Int {
return when ((roomId?.toList()?.sumBy { it.toInt() } ?: 0) % 3) {
1 -> R.color.riotx_avatar_fill_2
2 -> R.color.riotx_avatar_fill_3
else -> R.color.riotx_avatar_fill_1
}
}
/**
* Realm module for global classes
*/
@RealmModule(library = true,
classes = [
RawCacheEntity::class
])
internal class GlobalRealmModule

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2020 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.matrix.android.sdk.internal.raw
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Url
internal interface RawAPI {
@GET
fun getUrl(@Url url: String): Call<ResponseBody>
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2020 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.matrix.android.sdk.internal.raw
import com.zhuinden.monarchy.Monarchy
import dagger.Binds
import dagger.Lazy
import dagger.Module
import dagger.Provides
import io.realm.RealmConfiguration
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.internal.database.RealmKeysUtils
import org.matrix.android.sdk.internal.di.GlobalDatabase
import org.matrix.android.sdk.internal.di.MatrixScope
import org.matrix.android.sdk.internal.di.Unauthenticated
import org.matrix.android.sdk.internal.network.RetrofitFactory
@Module
internal abstract class RawModule {
@Module
companion object {
private const val DB_ALIAS = "matrix-sdk-global"
@JvmStatic
@Provides
@GlobalDatabase
fun providesMonarchy(@GlobalDatabase realmConfiguration: RealmConfiguration): Monarchy {
return Monarchy.Builder()
.setRealmConfiguration(realmConfiguration)
.build()
}
@JvmStatic
@Provides
@GlobalDatabase
@MatrixScope
fun providesRealmConfiguration(realmKeysUtils: RealmKeysUtils): RealmConfiguration {
return RealmConfiguration.Builder()
.apply {
realmKeysUtils.configureEncryption(this, DB_ALIAS)
}
.name("matrix-sdk-global.realm")
.modules(GlobalRealmModule())
.build()
}
@Provides
@JvmStatic
fun providesRawAPI(@Unauthenticated okHttpClient: Lazy<OkHttpClient>,
retrofitFactory: RetrofitFactory): RawAPI {
return retrofitFactory.create(okHttpClient, "https://example.org").create(RawAPI::class.java)
}
}
@Binds
abstract fun bindRawService(service: DefaultRawService): RawService
@Binds
abstract fun bindGetUrlTask(task: DefaultGetUrlTask): GetUrlTask
@Binds
abstract fun bindCleanRawCacheTask(task: DefaultCleanRawCacheTask): CleanRawCacheTask
}

View File

@@ -23,7 +23,7 @@ import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider
import arrow.core.Try
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.util.Cancelable
@@ -144,11 +144,13 @@ internal class DefaultFileService @Inject constructor(
if (elementToDecrypt != null) {
Timber.v("## FileService: decrypt file")
val decryptSuccess = MXEncryptedAttachments.decryptAttachment(
source.inputStream(),
elementToDecrypt,
destFile.outputStream().buffered()
)
val decryptSuccess = destFile.outputStream().buffered().use {
MXEncryptedAttachments.decryptAttachment(
source.inputStream(),
elementToDecrypt,
it
)
}
response.close()
if (!decryptSuccess) {
return@flatMap Try.Failure(IllegalStateException("Decryption error"))
@@ -172,7 +174,7 @@ internal class DefaultFileService @Inject constructor(
}
}
toNotify?.forEach { otherCallbacks ->
tryThis { otherCallbacks.onFailure(it) }
tryOrNull { otherCallbacks.onFailure(it) }
}
}, { file ->
callback.onSuccess(file)
@@ -184,7 +186,7 @@ internal class DefaultFileService @Inject constructor(
}
Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
toNotify?.forEach { otherCallbacks ->
tryThis { otherCallbacks.onSuccess(file) }
tryOrNull { otherCallbacks.onSuccess(file) }
}
})
}.toCancelable()

View File

@@ -166,8 +166,8 @@ internal class DefaultSession @Inject constructor(
SyncWorker.requireBackgroundSync(workManagerProvider, sessionId)
}
override fun startAutomaticBackgroundSync(repeatDelay: Long) {
SyncWorker.automaticallyBackgroundSync(workManagerProvider, sessionId, 0, repeatDelay)
override fun startAutomaticBackgroundSync(timeOutInSeconds: Long, repeatDelayInSeconds: Long) {
SyncWorker.automaticallyBackgroundSync(workManagerProvider, sessionId, timeOutInSeconds, repeatDelayInSeconds)
}
override fun stopAnyBackgroundSync() {

View File

@@ -47,6 +47,7 @@ import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorage
import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor
import org.matrix.android.sdk.internal.database.DatabaseCleaner
import org.matrix.android.sdk.internal.database.EventInsertLiveObserver
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory
import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.di.DeviceId
@@ -325,23 +326,27 @@ internal abstract class SessionModule {
@Binds
@IntoSet
abstract fun bindIntegrationManager(observer: IntegrationManager): SessionLifecycleObserver
abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindWidgetUrlFormatter(observer: DefaultWidgetURLFormatter): SessionLifecycleObserver
abstract fun bindWidgetUrlFormatter(formatter: DefaultWidgetURLFormatter): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindShieldTrustUpdated(observer: ShieldTrustUpdater): SessionLifecycleObserver
abstract fun bindShieldTrustUpdated(updater: ShieldTrustUpdater): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindIdentityService(observer: DefaultIdentityService): SessionLifecycleObserver
abstract fun bindIdentityService(service: DefaultIdentityService): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver
abstract fun bindDatabaseCleaner(cleaner: DatabaseCleaner): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindRealmSessionProvider(provider: RealmSessionProvider): SessionLifecycleObserver
@Binds
abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService

View File

@@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.session.call
import android.os.SystemClock
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.call.CallSignalingService
import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.CallsListener
@@ -210,7 +210,7 @@ internal class DefaultCallSignalingService @Inject constructor(
private fun onCallHangup(hangup: CallHangupContent) {
callListeners.toList().forEach {
tryThis {
tryOrNull {
it.onCallHangupReceived(hangup)
}
}
@@ -218,7 +218,7 @@ internal class DefaultCallSignalingService @Inject constructor(
private fun onCallAnswer(answer: CallAnswerContent) {
callListeners.toList().forEach {
tryThis {
tryOrNull {
it.onCallAnswerReceived(answer)
}
}
@@ -226,7 +226,7 @@ internal class DefaultCallSignalingService @Inject constructor(
private fun onCallManageByOtherSession(callId: String) {
callListeners.toList().forEach {
tryThis {
tryOrNull {
it.onCallManagedByOtherSession(callId)
}
}
@@ -237,7 +237,7 @@ internal class DefaultCallSignalingService @Inject constructor(
if (incomingCall.otherUserId == userId) return
callListeners.toList().forEach {
tryThis {
tryOrNull {
it.onCallInviteReceived(incomingCall, invite)
}
}
@@ -245,7 +245,7 @@ internal class DefaultCallSignalingService @Inject constructor(
private fun onCallIceCandidate(incomingCall: MxCall, candidates: CallCandidatesContent) {
callListeners.toList().forEach {
tryThis {
tryOrNull {
it.onCallIceCandidateReceived(incomingCall, candidates)
}
}

View File

@@ -32,7 +32,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink
import okio.source
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.network.ProgressRequestBody
@@ -96,7 +96,7 @@ internal class FileUploader @Inject constructor(@Authenticated
inputStream.copyTo(it)
}
return uploadFile(workingFile, filename, mimeType, progressListener).also {
tryThis { workingFile.delete() }
tryOrNull { workingFile.delete() }
}
}

View File

@@ -19,12 +19,10 @@ package org.matrix.android.sdk.internal.session.content
import android.content.Context
import android.graphics.BitmapFactory
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
@@ -34,13 +32,18 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.network.ProgressRequestBody
import org.matrix.android.sdk.internal.session.DefaultFileService
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker
import org.matrix.android.sdk.internal.session.room.send.LocalEchoIdentifiers
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import timber.log.Timber
import java.io.File
import java.util.UUID
@@ -56,12 +59,13 @@ private data class NewImageAttributes(
* Possible previous worker: None
* Possible next worker : Always [MultipleEventSendingDispatcherWorker]
*/
internal class UploadContentWorker(val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
internal class UploadContentWorker(val context: Context, params: WorkerParameters)
: SessionSafeCoroutineWorker<UploadContentWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
val events: List<Event>,
val localEchoIds: List<LocalEchoIdentifiers>,
val attachment: ContentAttachmentData,
val isEncrypted: Boolean,
val compressBeforeSending: Boolean,
@@ -73,20 +77,14 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
@Inject lateinit var fileService: DefaultFileService
@Inject lateinit var cancelSendTracker: CancelSendTracker
@Inject lateinit var imageCompressor: ImageCompressor
@Inject lateinit var localEchoRepository: LocalEchoRepository
override suspend fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success()
.also { Timber.e("Unable to parse work parameters") }
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
Timber.v("Starting upload media work with params $params")
if (params.lastFailureMessage != null) {
// Transmit the error
return Result.success(inputData)
.also { Timber.e("Work cancelled due to input error from parent") }
}
// Just defensive code to ensure that we never have an uncaught exception that could break the queue
return try {
internalDoWork(params)
@@ -96,11 +94,12 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
}
}
private suspend fun internalDoWork(params: Params): Result {
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
val allCancelled = params.events.all { cancelSendTracker.isCancelRequestedFor(it.eventId, it.roomId) }
private suspend fun internalDoWork(params: Params): Result {
val allCancelled = params.localEchoIds.all { cancelSendTracker.isCancelRequestedFor(it.eventId, it.roomId) }
if (allCancelled) {
// there is no point in uploading the image!
return Result.success(inputData)
@@ -214,18 +213,11 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
}
} catch (e: Exception) {
Timber.e(e, "## FileService: ERROR")
notifyTracker(params) { contentUploadStateTracker.setFailure(it, e) }
return Result.success(
WorkerParamsFactory.toData(
params.copy(
lastFailureMessage = e.localizedMessage
)
)
)
return handleFailure(params, e)
} finally {
// Delete all temporary files
filesToDelete.forEach {
tryThis { it.delete() }
tryOrNull { it.delete() }
}
}
}
@@ -289,46 +281,48 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
)
}
private fun handleSuccess(params: Params,
attachmentUrl: String,
encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String?,
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
newImageAttributes: NewImageAttributes?): Result {
private suspend fun handleSuccess(params: Params,
attachmentUrl: String,
encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String?,
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
newImageAttributes: NewImageAttributes?): Result {
notifyTracker(params) { contentUploadStateTracker.setSuccess(it) }
params.localEchoIds.forEach {
updateEvent(it.eventId, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newImageAttributes)
}
val updatedEvents = params.events
.map {
updateEvent(it, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newImageAttributes)
}
val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isEncrypted)
val sendParams = MultipleEventSendingDispatcherWorker.Params(
sessionId = params.sessionId,
localEchoIds = params.localEchoIds,
isEncrypted = params.isEncrypted
)
return Result.success(WorkerParamsFactory.toData(sendParams)).also {
Timber.v("## handleSuccess $attachmentUrl, work is stopped $isStopped")
}
}
private fun updateEvent(event: Event,
url: String,
encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String? = null,
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
newImageAttributes: NewImageAttributes?): Event {
val messageContent: MessageContent = event.content.toModel() ?: return event
val updatedContent = when (messageContent) {
is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newImageAttributes)
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
is MessageFileContent -> messageContent.update(url, encryptedFileInfo)
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo)
else -> messageContent
private suspend fun updateEvent(eventId: String,
url: String,
encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String? = null,
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
newImageAttributes: NewImageAttributes?) {
localEchoRepository.updateEcho(eventId) { _, event ->
val messageContent: MessageContent? = event.asDomain().content.toModel()
val updatedContent = when (messageContent) {
is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newImageAttributes)
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
is MessageFileContent -> messageContent.update(url, encryptedFileInfo)
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo)
else -> messageContent
}
event.content = ContentMapper.map(updatedContent.toContent())
}
return event.copy(content = updatedContent.toContent())
}
private fun notifyTracker(params: Params, function: (String) -> Unit) {
params.events
.mapNotNull { it.eventId }
.forEach { eventId -> function.invoke(eventId) }
params.localEchoIds.forEach { function.invoke(it.eventId) }
}
private fun MessageImageContent.update(url: String,

View File

@@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.session.download
import android.os.Handler
import android.os.Looper
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber
@@ -76,7 +76,7 @@ internal class DefaultContentDownloadStateTracker @Inject constructor() : Progre
Timber.v("## DL Progress Error code:$errorCode")
updateState(url, ContentDownloadStateTracker.State.Failure(errorCode))
listeners[url]?.forEach {
tryThis { it.onDownloadStateUpdate(ContentDownloadStateTracker.State.Failure(errorCode)) }
tryOrNull { it.onDownloadStateUpdate(ContentDownloadStateTracker.State.Failure(errorCode)) }
}
}
}
@@ -84,7 +84,7 @@ internal class DefaultContentDownloadStateTracker @Inject constructor() : Progre
private fun updateState(url: String, state: ContentDownloadStateTracker.State) {
states[url] = state
listeners[url]?.forEach {
tryThis { it.onDownloadStateUpdate(state) }
tryOrNull { it.onDownloadStateUpdate(state) }
}
}
}

View File

@@ -18,20 +18,19 @@
package org.matrix.android.sdk.internal.session.group
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import timber.log.Timber
import javax.inject.Inject
/**
* Possible previous worker: None
* Possible next worker : None
*/
internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
internal class GetGroupDataWorker(context: Context, params: WorkerParameters)
: SessionSafeCoroutineWorker<GetGroupDataWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
@@ -41,13 +40,11 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) :
@Inject lateinit var getGroupDataTask: GetGroupDataTask
override suspend fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure()
.also { Timber.e("Unable to parse work parameters") }
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
override suspend fun doSafeWork(params: Params): Result {
return runCatching {
getGroupDataTask.execute(GetGroupDataTask.Params.FetchAllActive)
}.fold(
@@ -55,4 +52,8 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) :
{ Result.retry() }
)
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -18,6 +18,7 @@
package org.matrix.android.sdk.internal.session.homeserver
import com.zhuinden.monarchy.Monarchy
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
@@ -32,7 +33,6 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.wellknown.GetWellknownTask
import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import java.util.Date
import javax.inject.Inject
@@ -109,16 +109,12 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {
homeServerCapabilitiesEntity.defaultIdentityServerUrl = getWellknownResult.identityServerUrl
homeServerCapabilitiesEntity.adminE2EByDefault = getWellknownResult.wellKnown.e2eAdminSetting?.e2eDefault ?: true
homeServerCapabilitiesEntity.preferredJitsiDomain = getWellknownResult.wellKnown.jitsiServer?.preferredDomain
// We are also checking for integration manager configurations
val config = configExtractor.extract(getWellknownResult.wellKnown)
if (config != null) {
Timber.v("Extracted integration config : $config")
realm.insertOrUpdate(config)
}
} else {
homeServerCapabilitiesEntity.adminE2EByDefault = true
}
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
}

View File

@@ -23,7 +23,7 @@ import androidx.lifecycle.LifecycleRegistry
import dagger.Lazy
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.extensions.tryThis
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.session.events.model.toModel
@@ -113,7 +113,7 @@ internal class DefaultIdentityService @Inject constructor(
// Url has changed, we have to reset our store, update internal configuration and notify listeners
identityStore.setUrl(baseUrl)
updateIdentityAPI(baseUrl)
listeners.toList().forEach { tryThis { it.onIdentityServerChange() } }
listeners.toList().forEach { tryOrNull { it.onIdentityServerChange() } }
}
}
@@ -236,7 +236,7 @@ internal class DefaultIdentityService @Inject constructor(
private suspend fun updateAccountData(url: String?) {
// Also notify the listener
withContext(coroutineDispatchers.main) {
listeners.toList().forEach { tryThis { it.onIdentityServerChange() } }
listeners.toList().forEach { tryOrNull { it.onIdentityServerChange() } }
}
updateUserAccountDataTask.execute(UpdateUserAccountDataTask.IdentityParams(

View File

@@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.internal.util.awaitTransaction
import timber.log.Timber
import javax.inject.Inject
@@ -39,7 +40,7 @@ internal class DefaultRefreshUserThreePidsTask @Inject constructor(private val p
Timber.d("Get ${accountThreePidsResponse.threePids?.size} threePids")
// Store the list in DB
monarchy.writeAsync { realm ->
monarchy.awaitTransaction { realm ->
realm.where(UserThreePidEntity::class.java).findAll().deleteAllFromRealm()
accountThreePidsResponse.threePids?.forEach {
val entity = UserThreePidEntity(

View File

@@ -17,10 +17,10 @@
package org.matrix.android.sdk.internal.session.pushers
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.pushers.PusherState
import org.matrix.android.sdk.internal.database.mapper.toEntity
@@ -28,16 +28,14 @@ import org.matrix.android.sdk.internal.database.model.PusherEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import javax.inject.Inject
internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<AddHttpPusherWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
@@ -50,14 +48,11 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
@Inject @SessionDatabase lateinit var monarchy: Monarchy
@Inject lateinit var eventBus: EventBus
override suspend fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure()
.also { Timber.e("Unable to parse work parameters") }
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val pusher = params.pusher
if (pusher.pushKey.isBlank()) {
@@ -82,6 +77,10 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
private suspend fun setPusher(pusher: JsonPusher) {
executeRequest<Unit>(eventBus) {
apiCall = pushersAPI.setPusher(pusher)

View File

@@ -17,17 +17,16 @@
package org.matrix.android.sdk.internal.session.room
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import io.realm.Realm
import javax.inject.Inject
internal interface RoomGetter {
@@ -38,18 +37,18 @@ internal interface RoomGetter {
@SessionScope
internal class DefaultRoomGetter @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider,
private val roomFactory: RoomFactory
) : RoomGetter {
override fun getRoom(roomId: String): Room? {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
return realmSessionProvider.withRealm { realm ->
createRoom(realm, roomId)
}
}
override fun getDirectRoomWith(otherUserId: String): Room? {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
return realmSessionProvider.withRealm { realm ->
RoomSummaryEntity.where(realm)
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)

View File

@@ -202,13 +202,13 @@ internal class DefaultRelationService @AssistedInject constructor(
private fun createEncryptEventWork(event: Event, keepKeys: List<String>?): OneTimeWorkRequest {
// Same parameter
val params = EncryptEventWorker.Params(sessionId, event, keepKeys)
val params = EncryptEventWorker.Params(sessionId, event.eventId!!, keepKeys)
val sendWorkData = WorkerParamsFactory.toData(params)
return timeLineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
}
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event)
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, eventId = event.eventId!!)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
}

View File

@@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.session.room.relation
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
@@ -27,45 +26,38 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
import org.matrix.android.sdk.api.session.room.model.relation.ReactionInfo
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
import org.matrix.android.sdk.internal.session.room.send.SendResponse
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import timber.log.Timber
import javax.inject.Inject
// TODO This is not used. Delete?
internal class SendRelationWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
internal class SendRelationWorker(context: Context, params: WorkerParameters)
: SessionSafeCoroutineWorker<SendRelationWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
val roomId: String,
val event: Event,
val eventId: String,
val relationType: String? = null,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@Inject lateinit var roomAPI: RoomAPI
@Inject lateinit var eventBus: EventBus
@Inject lateinit var localEchoRepository: LocalEchoRepository
override suspend fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure()
.also { Timber.e("Unable to parse work parameters") }
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
if (params.lastFailureMessage != null) {
// Transmit the error
return Result.success(inputData)
.also { Timber.e("Work cancelled due to input error from parent") }
}
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
val localEvent = params.event
if (localEvent.eventId == null) {
override suspend fun doSafeWork(params: Params): Result {
val localEvent = localEchoRepository.getUpToDateEcho(params.eventId)
if (localEvent?.eventId == null) {
return Result.failure()
}
val relationContent = localEvent.content.toModel<ReactionContent>()
@@ -88,6 +80,10 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) :
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
private suspend fun sendRelation(roomId: String, relationType: String, relatedEventId: String, localEvent: Event) {
executeRequest<SendResponse>(eventBus) {
apiCall = roomAPI.sendRelation(

View File

@@ -22,11 +22,13 @@ import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.Operation
import com.nikitakozlov.pury.annotations.StartProfiling
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
import org.matrix.android.sdk.api.session.events.model.isTextMessage
@@ -44,7 +46,6 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.CancelableBag
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.WorkManagerProvider
@@ -81,14 +82,21 @@ internal class DefaultSendService @AssistedInject constructor(
private val workerFutureListenerExecutor = Executors.newSingleThreadExecutor()
override fun sendEvent(eventType: String, content: JsonDict?): Cancelable {
override fun sendEvent(eventType: String, content: Content?, onBuiltEvent: ((Event) -> Unit)?): Cancelable {
return localEchoEventFactory.createEvent(roomId, eventType, content)
.also { onBuiltEvent?.invoke(it) }
.also { createLocalEcho(it) }
.let { sendEvent(it) }
}
override fun sendTextMessage(text: CharSequence, msgType: String, autoMarkdown: Boolean): Cancelable {
override fun sendTextMessage(
text: CharSequence,
msgType: String,
autoMarkdown: Boolean,
onBuiltEvent: ((Event) -> Unit)?
): Cancelable {
return localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown)
.also { onBuiltEvent?.invoke(it) }
.also { createLocalEcho(it) }
.let { sendEvent(it) }
}
@@ -106,8 +114,14 @@ internal class DefaultSendService @AssistedInject constructor(
}
}
override fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String): Cancelable {
override fun sendFormattedTextMessage(
text: String,
formattedText: String,
msgType: String,
onBuiltEvent: ((Event) -> Unit)?
): Cancelable {
return localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType)
.also { onBuiltEvent?.invoke(it) }
.also { createLocalEcho(it) }
.let { sendEvent(it) }
}
@@ -138,6 +152,7 @@ internal class DefaultSendService @AssistedInject constructor(
.let { timelineSendEventWorkCommon.postWork(roomId, it) }
}
@StartProfiling(profilerName = "Sending", stageName = "Send service", stageOrder = 0)
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable {
if (localEcho.root.isTextMessage() && localEcho.root.sendState.hasFailed()) {
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
@@ -336,7 +351,7 @@ internal class DefaultSendService @AssistedInject constructor(
private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
// Same parameter
return EncryptEventWorker.Params(sessionId, event)
return EncryptEventWorker.Params(sessionId, event.eventId ?: "")
.let { WorkerParamsFactory.toData(it) }
.let {
workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
@@ -360,7 +375,10 @@ internal class DefaultSendService @AssistedInject constructor(
attachment: ContentAttachmentData,
isRoomEncrypted: Boolean,
compressBeforeSending: Boolean): OneTimeWorkRequest {
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, allLocalEchos, attachment, isRoomEncrypted, compressBeforeSending)
val localEchoIds = allLocalEchos.map {
LocalEchoIdentifiers(it.roomId!!, it.eventId!!)
}
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, localEchoIds, attachment, isRoomEncrypted, compressBeforeSending)
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()

View File

@@ -18,21 +18,24 @@
package org.matrix.android.sdk.internal.session.room.send
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.send.SendPerformanceProfiler
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import timber.log.Timber
import javax.inject.Inject
@@ -41,12 +44,12 @@ import javax.inject.Inject
* Possible next worker : Always [SendEventWorker]
*/
internal class EncryptEventWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<EncryptEventWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
val event: Event,
val eventId: String,
/** Do not encrypt these keys, keep them as is in encrypted content (e.g. m.relates_to) */
val keepKeys: List<String>? = null,
override val lastFailureMessage: String? = null
@@ -56,24 +59,19 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
@Inject lateinit var localEchoRepository: LocalEchoRepository
@Inject lateinit var cancelSendTracker: CancelSendTracker
override suspend fun doWork(): Result {
Timber.v("Start Encrypt work")
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success()
.also { Timber.e("Unable to parse work parameters") }
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
Timber.v("## SendEvent: Start Encrypt work for event ${params.event.eventId}")
if (params.lastFailureMessage != null) {
// Transmit the error
return Result.success(inputData)
.also { Timber.e("Work cancelled due to input error from parent") }
}
override suspend fun doSafeWork(params: Params): Result {
Timber.v("## SendEvent: Start Encrypt work for event ${params.eventId}")
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
SendPerformanceProfiler.startStage(params.eventId, SendPerformanceProfiler.Stages.GET_UP_TO_DATE_ECHO)
val localEvent = localEchoRepository.getUpToDateEcho(params.eventId)
SendPerformanceProfiler.stopStage(params.eventId, SendPerformanceProfiler.Stages.GET_UP_TO_DATE_ECHO)
val localEvent = params.event
if (localEvent.eventId == null) {
SendPerformanceProfiler.startStage(params.eventId, SendPerformanceProfiler.Stages.ENCRYPT_WORKER)
if (localEvent?.eventId == null) {
return Result.success()
}
@@ -93,7 +91,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
var result: MXEncryptEventContentResult? = null
try {
result = awaitCallback {
crypto.encryptEventContent(localMutableContent, localEvent.type, localEvent.roomId!!, it)
crypto.encryptEventContent(localEvent.eventId, localMutableContent, localEvent.type, localEvent.roomId!!, it)
}
} catch (throwable: Throwable) {
error = throwable
@@ -106,15 +104,10 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
modifiedContent[toKeep] = it
}
}
val safeResult = result.copy(eventContent = modifiedContent)
val encryptedEvent = localEvent.copy(
type = safeResult.eventType,
content = safeResult.eventContent
)
// Better handling of local echo, to avoid decrypting transition on remote echo
// Should I only do it for text messages?
if (result.eventContent["algorithm"] == MXCRYPTO_ALGORITHM_MEGOLM) {
val decryptionLocalEcho = MXEventDecryptionResult(
val decryptionLocalEcho = if (result.eventContent["algorithm"] == MXCRYPTO_ALGORITHM_MEGOLM) {
MXEventDecryptionResult(
clearEvent = Event(
type = localEvent.type,
content = localEvent.content,
@@ -124,10 +117,18 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
senderCurve25519Key = result.eventContent["sender_key"] as? String,
claimedEd25519Key = crypto.getMyDevice().fingerprint()
)
localEchoRepository.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho)
} else {
null
}
val nextWorkerParams = SendEventWorker.Params(sessionId = params.sessionId, event = encryptedEvent)
localEchoRepository.updateEcho(localEvent.eventId) { _, localEcho ->
localEcho.type = EventType.ENCRYPTED
localEcho.content = ContentMapper.map(modifiedContent)
decryptionLocalEcho?.also {
localEcho.setDecryptionResult(it)
}
}
SendPerformanceProfiler.stopStage(params.eventId, SendPerformanceProfiler.Stages.ENCRYPT_WORKER)
val nextWorkerParams = SendEventWorker.Params(sessionId = params.sessionId, eventId = params.eventId)
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
} else {
val sendState = when (error) {
@@ -138,10 +139,14 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
// always return success, or the chain will be stuck for ever!
val nextWorkerParams = SendEventWorker.Params(
sessionId = params.sessionId,
event = localEvent,
eventId = localEvent.eventId,
lastFailureMessage = error?.localizedMessage ?: "Error"
)
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
import org.matrix.android.sdk.api.session.room.model.relation.ReactionInfo
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent
import org.matrix.android.sdk.api.session.room.send.SendPerformanceProfiler
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.isReply
@@ -325,6 +326,7 @@ internal class LocalEchoEventFactory @Inject constructor(
fun createEvent(roomId: String, type: String, content: Content?): Event {
val localId = LocalEcho.createLocalEchoId()
SendPerformanceProfiler.startProfiling(localId)
return Event(
roomId = roomId,
originServerTs = dummyOriginServerTs(),
@@ -433,10 +435,10 @@ internal class LocalEchoEventFactory @Inject constructor(
TextContent(content.body, formattedText)
}
}
MessageType.MSGTYPE_FILE -> return TextContent("sent a file.")
MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
MessageType.MSGTYPE_FILE -> return TextContent("sent a file.")
MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
else -> return TextContent(content?.body ?: "")
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.room.send
import com.squareup.moshi.JsonClass
/**
* This is used as a holder to pass necessary data to some workers params.
*/
@JsonClass(generateAdapter = true)
internal data class LocalEchoIdentifiers(val roomId: String, val eventId: String)

View File

@@ -18,18 +18,21 @@
package org.matrix.android.sdk.internal.session.room.send
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.session.events.model.Content
import io.realm.Realm
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.send.SendPerformanceProfiler
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.asyncTransaction
import org.matrix.android.sdk.internal.database.helper.nextId
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertEntity
@@ -42,13 +45,14 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.awaitTransaction
import io.realm.Realm
import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import javax.inject.Inject
internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val realmSessionProvider: RealmSessionProvider,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val eventBus: EventBus,
private val timelineEventMapper: TimelineEventMapper) {
@@ -59,7 +63,8 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
if (event.eventId == null) {
throw IllegalStateException("You should have set an eventId for your event")
}
val timelineEventEntity = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
// SendPerformanceProfiler.startStage(event.eventId, SendPerformanceProfiler.Stages.LOCAL_ECHO)
val timelineEventEntity = realmSessionProvider.withRealm { realm ->
val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
val roomMemberHelper = RoomMemberHelper(realm, roomId)
val myUser = roomMemberHelper.getLastRoomMember(senderId)
@@ -75,43 +80,55 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
}
val timelineEvent = timelineEventMapper.map(timelineEventEntity)
eventBus.post(DefaultTimeline.OnLocalEchoCreated(roomId = roomId, timelineEvent = timelineEvent))
monarchy.writeAsync { realm ->
taskExecutor.executorScope.asyncTransaction(monarchy) { realm ->
val eventInsertEntity = EventInsertEntity(event.eventId, event.type).apply {
this.insertType = EventInsertType.LOCAL_ECHO
}
realm.insert(eventInsertEntity)
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return@writeAsync
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return@asyncTransaction
roomEntity.sendingTimelineEvents.add(0, timelineEventEntity)
roomSummaryUpdater.updateSendingInformation(realm, roomId)
//SendPerformanceProfiler.stopStage(event.eventId, SendPerformanceProfiler.Stages.LOCAL_ECHO)
}
}
fun updateSendState(eventId: String, sendState: SendState) {
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Update local state of $eventId to ${sendState.name}")
monarchy.writeAsync { realm ->
updateEchoAsync(eventId) { realm, sendingEventEntity ->
if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) {
// If already synced, do not put as sent
} else {
sendingEventEntity.sendState = sendState
}
roomSummaryUpdater.updateSendingInformation(realm, sendingEventEntity.roomId)
}
}
suspend fun updateEcho(eventId: String, block: (realm: Realm, eventEntity: EventEntity) -> Unit) {
monarchy.awaitTransaction { realm ->
val sendingEventEntity = EventEntity.where(realm, eventId).findFirst()
if (sendingEventEntity != null) {
if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) {
// If already synced, do not put as sent
} else {
sendingEventEntity.sendState = sendState
}
roomSummaryUpdater.updateSendingInformation(realm, sendingEventEntity.roomId)
block(realm, sendingEventEntity)
}
}
}
fun updateEncryptedEcho(eventId: String, encryptedContent: Content, mxEventDecryptionResult: MXEventDecryptionResult) {
monarchy.writeAsync { realm ->
fun updateEchoAsync(eventId: String, block: (realm: Realm, eventEntity: EventEntity) -> Unit) {
taskExecutor.executorScope.asyncTransaction(monarchy) { realm ->
val sendingEventEntity = EventEntity.where(realm, eventId).findFirst()
if (sendingEventEntity != null) {
sendingEventEntity.type = EventType.ENCRYPTED
sendingEventEntity.content = ContentMapper.map(encryptedContent)
sendingEventEntity.setDecryptionResult(mxEventDecryptionResult)
block(realm, sendingEventEntity)
}
}
}
suspend fun getUpToDateEcho(eventId: String): Event? {
// We are using awaitTransaction here to make sure this executes after other transactions
return monarchy.awaitTransaction { realm ->
EventEntity.where(realm, eventId).findFirst()?.asDomain(castJsonNumbers = true)
}
}
suspend fun deleteFailedEcho(roomId: String, localEcho: TimelineEvent) {
deleteFailedEcho(roomId, localEcho.eventId)
}
@@ -149,8 +166,8 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
return getAllEventsWithStates(roomId, SendState.HAS_FAILED_STATES)
}
fun getAllEventsWithStates(roomId: String, states : List<SendState>): List<TimelineEvent> {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
fun getAllEventsWithStates(roomId: String, states: List<SendState>): List<TimelineEvent> {
return realmSessionProvider.withRealm { realm ->
TimelineEventEntity
.findAllInRoomWithSendStates(realm, roomId, states)
.sortedByDescending { it.displayIndex }

View File

@@ -19,18 +19,17 @@ package org.matrix.android.sdk.internal.session.room.send
import android.content.Context
import androidx.work.BackoffPolicy
import androidx.work.CoroutineWorker
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
import org.matrix.android.sdk.internal.session.room.timeline.TimelineSendEventWorkCommon
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import org.matrix.android.sdk.internal.worker.startChain
import timber.log.Timber
import java.util.concurrent.TimeUnit
@@ -43,12 +42,12 @@ import javax.inject.Inject
* Possible next worker : None, but it will post new work to send events, encrypted or not
*/
internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
: SessionSafeCoroutineWorker<MultipleEventSendingDispatcherWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
val events: List<Event>,
val localEchoIds: List<LocalEchoIdentifiers>,
val isEncrypted: Boolean,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@@ -57,46 +56,48 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
@Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
@Inject lateinit var localEchoRepository: LocalEchoRepository
override suspend fun doWork(): Result {
Timber.v("## SendEvent: Start dispatch sending multiple event work")
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.success()
.also { Timber.e("Unable to parse work parameters") }
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
if (params.lastFailureMessage != null) {
params.events.forEach { event ->
event.eventId?.let { localEchoRepository.updateSendState(it, SendState.UNDELIVERED) }
}
// Transmit the error if needed?
return Result.success(inputData)
.also { Timber.e("## SendEvent: Work cancelled due to input error from parent ${params.lastFailureMessage}") }
override fun doOnError(params: Params): Result {
params.localEchoIds.forEach { localEchoIds ->
localEchoRepository.updateSendState(localEchoIds.eventId, SendState.UNDELIVERED)
}
return super.doOnError(params)
}
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
Timber.v("## SendEvent: Start dispatch sending multiple event work")
// Create a work for every event
params.events.forEach { event ->
params.localEchoIds.forEach { localEchoIds ->
val roomId = localEchoIds.roomId
val eventId = localEchoIds.eventId
if (params.isEncrypted) {
localEchoRepository.updateSendState(event.eventId ?: "", SendState.ENCRYPTING)
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule encrypt and send event ${event.eventId}")
val encryptWork = createEncryptEventWork(params.sessionId, event, true)
localEchoRepository.updateSendState(eventId, SendState.ENCRYPTING)
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule encrypt and send event $eventId")
val encryptWork = createEncryptEventWork(params.sessionId, eventId, true)
// Note that event will be replaced by the result of the previous work
val sendWork = createSendEventWork(params.sessionId, event, false)
timelineSendEventWorkCommon.postSequentialWorks(event.roomId!!, encryptWork, sendWork)
val sendWork = createSendEventWork(params.sessionId, eventId, false)
timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork)
} else {
localEchoRepository.updateSendState(event.eventId ?: "", SendState.SENDING)
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule send event ${event.eventId}")
val sendWork = createSendEventWork(params.sessionId, event, true)
timelineSendEventWorkCommon.postWork(event.roomId!!, sendWork)
localEchoRepository.updateSendState(eventId, SendState.SENDING)
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule send event $eventId")
val sendWork = createSendEventWork(params.sessionId, eventId, true)
timelineSendEventWorkCommon.postWork(roomId, sendWork)
}
}
return Result.success()
}
private fun createEncryptEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest {
val params = EncryptEventWorker.Params(sessionId, event)
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
private fun createEncryptEventWork(sessionId: String, eventId: String, startChain: Boolean): OneTimeWorkRequest {
val params = EncryptEventWorker.Params(sessionId, eventId)
val sendWorkData = WorkerParamsFactory.toData(params)
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
@@ -107,8 +108,8 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
.build()
}
private fun createSendEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event)
private fun createSendEventWork(sessionId: String, eventId: String, startChain: Boolean): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, eventId = eventId)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)

View File

@@ -17,24 +17,24 @@
package org.matrix.android.sdk.internal.session.room.send
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.getSessionComponent
import org.greenrobot.eventbus.EventBus
import timber.log.Timber
import javax.inject.Inject
/**
* Possible previous worker: None
* Possible next worker : None
*/
internal class RedactEventWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
internal class RedactEventWorker(context: Context, params: WorkerParameters)
: SessionSafeCoroutineWorker<RedactEventWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
@@ -49,20 +49,11 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters) : C
@Inject lateinit var roomAPI: RoomAPI
@Inject lateinit var eventBus: EventBus
override suspend fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure()
.also { Timber.e("Unable to parse work parameters") }
if (params.lastFailureMessage != null) {
// Transmit the error
return Result.success(inputData)
.also { Timber.e("Work cancelled due to input error from parent") }
}
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
sessionComponent.inject(this)
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val eventId = params.eventId
return runCatching {
executeRequest<SendResponse>(eventBus) {
@@ -91,4 +82,8 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters) : C
}
)
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -37,6 +37,7 @@ internal class RoomEventSender @Inject constructor(
@SessionId private val sessionId: String,
private val cryptoService: CryptoService
) {
fun sendEvent(event: Event): Cancelable {
// Encrypted room handling
return if (cryptoService.isRoomEncrypted(event.roomId ?: "")
@@ -56,7 +57,7 @@ internal class RoomEventSender @Inject constructor(
private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
// Same parameter
val params = EncryptEventWorker.Params(sessionId, event)
val params = EncryptEventWorker.Params(sessionId, event.eventId!!)
val sendWorkData = WorkerParamsFactory.toData(params)
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
@@ -68,7 +69,7 @@ internal class RoomEventSender @Inject constructor(
}
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event)
val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, eventId = event.eventId!!)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)

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