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

Compare commits

...

851 Commits

Author SHA1 Message Date
Benoit Marty
997101a44b Merge branch 'hotfix/crash_locales' 2020-05-28 11:31:38 +02:00
Benoit Marty
ec1422b0f0 Fix crash due to bad script. Tha bad value has already been fixed on Weblate. 2020-05-28 11:27:11 +02:00
Benoit Marty
d1c4d4b099 Merge branch 'release/0.21.0' 2020-05-28 10:43:55 +02:00
Benoit Marty
9a972b2f73 Prepare release 0.21.0 2020-05-28 10:43:40 +02:00
Benoit Marty
116bab5bc8 Fix call event not rendered in e2e rooms. 2020-05-27 19:02:27 +02:00
Benoit Marty
c76eb3bc98 Fix lint issue 2020-05-27 18:58:45 +02:00
Benoit Marty
81c1717384 Format strings 2020-05-27 18:49:34 +02:00
Benoit Marty
0fd0500d30 Merge pull request #1414 from RiotTranslateBot/weblate-riot-android-riotx-application
Update from Weblate
2020-05-27 17:50:06 +02:00
Benoit Marty
34d638da4f Merge pull request #1411 from vector-im/feature/attachment_e2e
Feature/attachment e2e
2020-05-27 15:13:17 +02:00
Weblate
e39b177b5b Merge branch 'origin/develop' into Weblate. 2020-05-27 13:04:35 +00:00
LinAGKar
07aa3ee64c Translated using Weblate (Swedish)
Currently translated at 19.9% (335 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-27 13:04:29 +00:00
yuuki-san
c94856cdf8 Translated using Weblate (Slovak)
Currently translated at 95.7% (156 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/sk/
2020-05-27 13:04:26 +00:00
yuuki-san
4dd0c04537 Translated using Weblate (Slovak)
Currently translated at 53.9% (906 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sk/
2020-05-27 13:04:26 +00:00
Амёба
99c409b6d2 Translated using Weblate (Russian)
Currently translated at 82.6% (1390 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/ru/
2020-05-27 13:04:26 +00:00
Marko Dimjašević
8cb2c2532f Translated using Weblate (Croatian)
Currently translated at 54.2% (912 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hr/
2020-05-27 13:04:24 +00:00
Benoit Marty
6f804cab4d Avoid duplicated events in DB (one with localId and one with eventId from homeserver, once synced) 2020-05-27 11:27:54 +02:00
Benoit Marty
cf3dbb378e Get uploaded files for e2e rooms, from local DB 2020-05-26 22:06:51 +02:00
Benoit Marty
0eb36a607b Merge pull request #1402 from vector-im/feature/rageshake_dialogs
Feature/rageshake dialogs
2020-05-26 00:04:33 +02:00
Benoit Marty
0509e76f18 var -> val 2020-05-25 23:54:53 +02:00
Benoit Marty
e379ccf086 Extract MatrixConfiguration to its own file, for a better visibility 2020-05-25 23:53:36 +02:00
Benoit Marty
7ae52d676d Use directly java.net.proxy class 2020-05-25 23:50:10 +02:00
Benoit Marty
3d33018ffa Merge pull request #1147 from unclejay80/http_proxy_init
added network proxy configuration
2020-05-25 23:43:11 +02:00
Benoit Marty
860595520b Merge pull request #1401 from vector-im/feature/cleanup
Small PR with code cleanup
2020-05-25 17:30:31 +02:00
Benoit Marty
ae318a835e Merge pull request #1387 from vector-im/feature/attachments_list
Attachments list
2020-05-25 17:29:54 +02:00
Benoit Marty
7a3dbecc08 Fixes #860 2020-05-25 17:05:17 +02:00
Benoit Marty
c52aae7c29 Uploads: Snackbar 2020-05-25 17:05:17 +02:00
Benoit Marty
f0f3e8ddb9 Uploads: auto-review 2020-05-25 17:05:17 +02:00
Benoit Marty
a95102a78f Uploads: Use StateView for better Loading/Empty rendering 2020-05-25 17:02:57 +02:00
Benoit Marty
2adafbeb03 Uploads: use SenderInfo in TimelineEvent 2020-05-25 17:02:57 +02:00
Benoit Marty
f3a5fb7fe3 Uploads: rework: provide information about the sender 2020-05-25 16:54:08 +02:00
Benoit Marty
907a786b1a Uploads: load element until loader not displayed anymore 2020-05-25 16:54:08 +02:00
Benoit Marty
e3ed3e5b05 Uploads: cleanup 2020-05-25 16:54:08 +02:00
Benoit Marty
a2b366ebfe Uploads: add placeholder for images 2020-05-25 16:54:08 +02:00
Benoit Marty
f7de2f0f13 Uploads: create extension 2020-05-25 16:54:08 +02:00
Benoit Marty
919225bdfd Uploads: create extension 2020-05-25 16:54:08 +02:00
Benoit Marty
88cba74cac Uploads: add screen - WIP 2020-05-25 16:54:08 +02:00
Benoit Marty
e9ca876444 Uploads: add screen - WIP 2020-05-25 16:54:08 +02:00
Benoit Marty
0992e76800 Uploads: add screen - WIP 2020-05-25 16:54:08 +02:00
Benoit Marty
8a9498bae4 Uploads: add the service and the task 2020-05-25 16:54:08 +02:00
Weblate
d2598480c8 var -> val 2020-05-25 16:54:08 +02:00
Benoit Marty
6e57b06673 Ensure Filter model match the spec and add Javadoc 2020-05-25 16:54:08 +02:00
Benoit Marty
a036be6436 Fix missing title in BugReport screen 2020-05-25 15:42:58 +02:00
Benoit Marty
53c7ea2831 Kotlin: use orEmpty() for Maps 2020-05-25 15:42:30 +02:00
Benoit Marty
e117fec74f Kotlin: use orEmpty() for Maps 2020-05-25 14:07:25 +02:00
Benoit Marty
6e01b75b2f Dagger: use generic name for parameters 2020-05-25 14:04:36 +02:00
Benoit Marty
691e7fe616 Kotlin: use orEmpty() 2020-05-25 14:04:36 +02:00
Benoit Marty
e793a46576 Merge pull request #1390 from vector-im/feature/icon
Change icon to magnifying-glass to filter room (#1384)
2020-05-25 10:57:30 +02:00
Benoit Marty
fa48e8cffa Change icon to magnifying-glass to filter room (#1384) 2020-05-25 10:57:14 +02:00
Benoit Marty
69cf437a39 Merge pull request #1291 from vector-im/feature/complete_security
Add hint to translators
2020-05-25 10:28:20 +02:00
Benoit Marty
ebbc432570 Merge pull request #1398 from vector-im/feature/date_time
Bugfixes
2020-05-25 10:13:30 +02:00
LinAGKar
2532724e92 Translated using Weblate (Swedish)
Currently translated at 18.8% (316 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-25 00:29:11 +00:00
MamasLT
e31693b4b7 Translated using Weblate (Lithuanian)
Currently translated at 2.5% (4 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/lt/
2020-05-25 00:29:10 +00:00
Marko Dimjašević
3ed6452232 Translated using Weblate (Croatian)
Currently translated at 50.9% (856 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hr/
2020-05-25 00:29:10 +00:00
Besnik Bleta
166aaa62f0 Translated using Weblate (Albanian)
Currently translated at 99.5% (1673 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sq/
2020-05-25 00:29:00 +00:00
MamasLT
45e5fff622 Added translation using Weblate (Lithuanian) 2020-05-23 23:44:45 +00:00
MamasLT
8f2dba09ee Added translation using Weblate (Lithuanian) 2020-05-23 23:43:32 +00:00
LinAGKar
2ea46c5e54 Translated using Weblate (Swedish)
Currently translated at 18.7% (315 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-23 14:29:20 +00:00
@a2sc:matrix.org
fc5f0f7673 Translated using Weblate (German)
Currently translated at 99.5% (1674 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-23 14:29:19 +00:00
Tirifto
188f4a2e72 Translated using Weblate (Esperanto)
Currently translated at 27.5% (463 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/eo/
2020-05-23 14:29:18 +00:00
Marko Dimjašević
cf97fc3b01 Translated using Weblate (Croatian)
Currently translated at 41.2% (693 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hr/
2020-05-23 14:29:15 +00:00
Osoitz
5267ba240a Translated using Weblate (Basque)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/eu/
2020-05-23 14:29:00 +00:00
Osoitz
f185dcacd7 Translated using Weblate (Basque)
Currently translated at 100.0% (1682 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/eu/
2020-05-23 14:29:00 +00:00
Benoit Marty
330a33a0e8 Render formatted_body for m.notice and m.emote (Fixes #1196) 2020-05-21 01:47:17 +02:00
Benoit Marty
b75b299847 Create MessageContentWithFormattedBody interface 2020-05-21 01:05:47 +02:00
Benoit Marty
7c59bcc928 Only "org.matrix.custom.html" is supported 2020-05-21 00:41:36 +02:00
Benoit Marty
0e110b0794 Cleanup: use existing TextContent class instead of Pair<> 2020-05-21 00:40:12 +02:00
Benoit Marty
e156a62e19 Enable markdown (if active) when sending emote (Fixes #734) 2020-05-21 00:27:19 +02:00
Benoit Marty
628439aa65 Mardown: sending "**text in bold** was sending extra paragraph and extra new line 2020-05-21 00:25:59 +02:00
Benoit Marty
d49fcb80fc "Seen by" uses 12h time (Fixes #1378)
DateUtils.FORMAT_SHOW_TIME has to be used for i18n to be effective on DateUtils.getRelativeDateTimeString(), do not ask me why.
Also internal switching of language does not have effect on this method, you'll have to restart the application.
2020-05-20 23:52:41 +02:00
Benoit Marty
ca37895619 Merge pull request #1374 from vector-im/feature/sas_v2
support new key agreement method for SAS
2020-05-20 18:32:01 +02:00
Benoit Marty
43497e0da9 Merge pull request #1315 from vector-im/feature/forward_pagination
Feature/forward pagination
2020-05-20 18:30:09 +02:00
LinAGKar
efa510c0a8 Translated using Weblate (Swedish)
Currently translated at 17.1% (287 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-20 15:29:38 +00:00
yuuki-san
0828159ee4 Translated using Weblate (Slovak)
Currently translated at 53.9% (906 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sk/
2020-05-20 15:29:35 +00:00
Амёба
90f21198c3 Translated using Weblate (Russian)
Currently translated at 82.1% (1381 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/ru/
2020-05-20 15:29:34 +00:00
Eduardo F
a0b0778fce Translated using Weblate (Portuguese (Brazil))
Currently translated at 52.2% (878 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/pt_BR/
2020-05-20 15:29:33 +00:00
random
d937fa67ad Translated using Weblate (Italian)
Currently translated at 100.0% (1682 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/it/
2020-05-20 15:29:33 +00:00
Kim Brose
91d396fbca Translated using Weblate (German)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/de/
2020-05-20 15:29:32 +00:00
@a2sc:matrix.org
afba5b2b6c Translated using Weblate (German)
Currently translated at 99.5% (1673 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-20 15:29:32 +00:00
Benoit Marty
b579a1bc83 Translated using Weblate (French)
Currently translated at 100.0% (1682 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-20 15:29:31 +00:00
Priit Jõerüüt
db49673fc7 Translated using Weblate (Estonian)
Currently translated at 14.2% (239 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/et/
2020-05-20 15:29:31 +00:00
Marko Dimjašević
bb0511e659 Translated using Weblate (Croatian)
Currently translated at 26.2% (441 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hr/
2020-05-20 15:29:22 +00:00
Jeff Huang
0e0763ec2d Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1682 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/zh_Hant/
2020-05-20 15:28:52 +00:00
ganfra
e1c6542e03 Merge branch 'develop' into feature/forward_pagination 2020-05-19 15:25:33 +02:00
ganfra
01484978bd Fix lint 2020-05-19 15:24:36 +02:00
ganfra
cad14c93d0 Timeline: fix tests and add message order check 2020-05-19 14:39:42 +02:00
toastbroot
ee52b9185b Translated using Weblate (German)
Currently translated at 99.4% (1672 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-19 09:33:16 +00:00
Valere
861a7f791f Update change log 2020-05-19 09:57:54 +02:00
Valere
f2fa57224b Update matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt
Co-authored-by: Hubert Chathi <hubert@uhoreg.ca>
2020-05-19 09:55:45 +02:00
Valere
e0977dd97b Add new key agreement protocol 2020-05-19 09:55:45 +02:00
Benoit Marty
f47bef71a4 Merge pull request #1354 from vector-im/feature/identity
Identity server
2020-05-18 17:28:57 +02:00
Benoit Marty
92985fc8e7 Split long line 2020-05-18 17:25:15 +02:00
Benoit Marty
243b0a7d82 ktlint 2020-05-18 17:23:33 +02:00
Benoit Marty
92c719a803 Set identity server: do not show the error in the EditText when user want to use the default identity server 2020-05-18 17:23:33 +02:00
Benoit Marty
f4108ae0eb Properly handle Loading state: button were still active... Also finish the work on the controller 2020-05-18 17:23:33 +02:00
Benoit Marty
ecf3fee709 Integrate Valere's remarks - step 3: use viewModelScope in ViewModels 2020-05-18 17:23:33 +02:00
Benoit Marty
e67e472025 Integrate Valere's remarks - step 2: Stop using (or at least reduce usage of) GlobalScope 2020-05-18 17:23:33 +02:00
Benoit Marty
a6541481bf Integrate Valere's remarks - step 1 2020-05-18 17:23:33 +02:00
Benoit Marty
85a4f83662 Colorize the identity server url 2020-05-18 17:23:33 +02:00
Benoit Marty
22955e6b34 Use debouncedClicks every where, for faster UI (it uses throttleFirst operator instead of debounce) 2020-05-18 17:23:33 +02:00
Benoit Marty
9520aff848 Use debouncedClicks every where, for faster UI (it uses throttleFirst operator instead of debounce) 2020-05-18 17:23:33 +02:00
Benoit Marty
6b09a78ece Identity: Improve identity choice screen after review 2020-05-18 17:23:33 +02:00
Benoit Marty
789bcc8d77 Identity: Bugfix: do not fail when trying to disconnect the current identity server, if there is no token available 2020-05-18 17:23:33 +02:00
Benoit Marty
2914117a8e Move some classes 2020-05-18 17:23:33 +02:00
Benoit Marty
8049962a99 Create a Wellknown module, because both AuthModule and HomeServerCapability module need it 2020-05-18 17:23:33 +02:00
Benoit Marty
225b1c380e Identity: retrieve the default identity server url 2020-05-18 17:23:33 +02:00
Benoit Marty
60d80ea0ba Fix compilation issue after rebase 2020-05-18 17:23:33 +02:00
Benoit Marty
c8211098f3 Identity: The store has to be migrated properly because it contains user's data 2020-05-18 17:23:33 +02:00
Benoit Marty
e78fde4eca Identity: rename a few class and add a mapper to avoid using Entities in the code 2020-05-18 17:23:33 +02:00
Benoit Marty
59d60813fb Cleanup 2020-05-18 17:23:33 +02:00
Benoit Marty
4c31e52892 Add facilities and Javadoc on SessionParams data class 2020-05-18 17:23:33 +02:00
Benoit Marty
c646fd2b36 ktlint 2020-05-18 17:23:33 +02:00
Benoit Marty
6432859732 Avoid code duplication 2020-05-18 17:23:33 +02:00
Benoit Marty
2beef7d816 Identity: fix issue with logout request.
Also disconnect previous set identity server when changing url, when disconnecting, and when deactivating account
2020-05-18 17:23:33 +02:00
Benoit Marty
623056455b Identity: fix rendering error 2020-05-18 17:23:33 +02:00
Benoit Marty
7a4d9370e3 Identity: human readable errors 2020-05-18 17:23:33 +02:00
Benoit Marty
fe3138492e Identity: fix issue in dark theme 2020-05-18 17:23:33 +02:00
Benoit Marty
05a52164f3 Identity: Changelog and documentation 2020-05-18 17:23:33 +02:00
Benoit Marty
d14f1dd1ab Capability: do not update data if the corresponding request fails 2020-05-18 17:23:13 +02:00
Benoit Marty
88e8c11ee5 Identity: protect against outdated homeserver 2020-05-18 17:23:13 +02:00
Benoit Marty
7afc7bdb31 Identity refresh main setting page. 2020-05-18 17:23:13 +02:00
Benoit Marty
84a3754c9f Confirm identity server disconnection in all cases, as Riot-Web does 2020-05-18 17:23:13 +02:00
Benoit Marty
4b2f8e9174 Auto-review 2020-05-18 17:23:13 +02:00
Benoit Marty
a17932e17e Add missing internal 2020-05-18 17:23:13 +02:00
Benoit Marty
084c27a2bb Identity: cleanup 2020-05-18 17:23:13 +02:00
Benoit Marty
ed2f62cbe7 Identity: ping API V2 and cleanup 2020-05-18 17:23:13 +02:00
Benoit Marty
38fb7185b6 Identity: One class per file 2020-05-18 17:23:13 +02:00
Benoit Marty
ce42d2fb8a Identity: improve terms not signed case 2020-05-18 17:23:13 +02:00
Benoit Marty
af3fc22e2d Identity: correctly handle terms update 2020-05-18 17:22:40 +02:00
Benoit Marty
b659cb60a2 Improve code 2020-05-18 17:22:40 +02:00
Benoit Marty
34cf9903dc Improve code 2020-05-18 17:22:40 +02:00
Benoit Marty
062a21e39a Improve code 2020-05-18 17:22:40 +02:00
Benoit Marty
4510aff00a ktlint 2020-05-18 17:22:40 +02:00
Benoit Marty
d0953b8406 Identity: Cleanup 2020-05-18 17:22:40 +02:00
Benoit Marty
7822660ce7 Identity: Extract enum for Epoxy Items 2020-05-18 17:22:40 +02:00
Benoit Marty
bdfcf5c67c Identity: cleanup Epoxy items 2020-05-18 17:22:40 +02:00
Benoit Marty
ae0d09a049 Identity: cancel binding WIP 2020-05-18 17:22:40 +02:00
Benoit Marty
69759b7415 Identity: store sendAttempt in DB 2020-05-18 17:22:40 +02:00
Benoit Marty
7e8e1ab9b7 Identity: change DB and add sendAttempt 2020-05-18 17:22:40 +02:00
Benoit Marty
b44f5d3b4a Handle correctly the verification code error case 2020-05-18 17:22:40 +02:00
Benoit Marty
03f8b66993 Remove undocumented parameter
https://github.com/matrix-org/sydent/issues/195
2020-05-18 17:22:40 +02:00
Benoit Marty
e411f139c8 Identity: validate code received by SMS 2020-05-18 17:22:40 +02:00
Benoit Marty
e962d1dadf Small improvement 2020-05-18 17:22:40 +02:00
Benoit Marty
756b0febe6 Identity: Add some doc 2020-05-18 17:22:40 +02:00
Benoit Marty
1535f3e2e5 Identity: bind/unbnd: restore the bind in progress State 2020-05-18 17:22:40 +02:00
Benoit Marty
3e808dec90 Identity: bind/unbnd WIP 2020-05-18 17:22:40 +02:00
Benoit Marty
637f4a8350 Fix small UI bug 2020-05-18 17:22:40 +02:00
Benoit Marty
d3bc9f52fd Remove duplicated class 2020-05-18 17:22:40 +02:00
Benoit Marty
ffd8ac859d Identity: fix sync of indetity server change 2020-05-18 17:22:40 +02:00
Benoit Marty
6e43e9b51c Identity: refresh pepper, logout feature and other improvements 2020-05-18 17:22:40 +02:00
Benoit Marty
426171508e Import and adapt Terms Of Service management: SDK and UI (compiling) - still fixing issue 2020-05-18 17:22:40 +02:00
Benoit Marty
e86460b578 Import and adapt Terms Of Service management: SDK and UI (compiling) 2020-05-18 17:22:40 +02:00
Benoit Marty
8dd5f88dba Identity: cleanup UI 2020-05-18 17:21:59 +02:00
Benoit Marty
3aa6de7cf5 Identity: progressing 2020-05-18 17:21:59 +02:00
Benoit Marty
a75242c79d Retrieve ThreePids from homeserver 2020-05-18 17:21:59 +02:00
Benoit Marty
784918350b Identity: import UI/UX From Riot and adapt to RiotX architecture 2020-05-18 17:21:59 +02:00
Benoit Marty
0199cf9a03 Identity - Fix issue with Realm 2020-05-18 17:21:59 +02:00
Benoit Marty
ab6e7a3b8a Identity - WIP (compilation ok) 2020-05-18 17:21:59 +02:00
Benoit Marty
f489265ce7 Create AccessTokenProvider 2020-05-18 17:21:59 +02:00
Benoit Marty
6c9c3e5cb3 To merge with previous previous commit 2020-05-18 17:21:59 +02:00
Benoit Marty
9b7c2599a7 Add withOlmUtility facility 2020-05-18 17:21:59 +02:00
Benoit Marty
25bbd7c526 Identity - Create DB 2020-05-18 17:21:59 +02:00
Benoit Marty
e0c3f3638d Merge pull request #1370 from vector-im/feature/redacted_message
Feature/redacted message
2020-05-18 16:30:40 +02:00
Benoit Marty
d45653dbb3 Ganfra's review: Improve the filters declaration 2020-05-18 16:26:18 +02:00
Benoit Marty
f70623beea Ganfra's review: Handle filterRedacted in TimelineHiddenReadReceipts 2020-05-18 16:26:18 +02:00
Benoit Marty
e542e4ba22 Add a setting to hide redacted events (#951) 2020-05-18 16:26:18 +02:00
Benoit Marty
05d1e64cb5 New rendering for redacted message 2020-05-18 16:25:54 +02:00
Benoit Marty
adac80062c Merge pull request #1368 from vector-im/feature/switch_language
Feature/switch language
2020-05-18 16:22:07 +02:00
Benoit Marty
28f8d9500e Better coroutine management 2020-05-18 16:21:47 +02:00
Benoit Marty
538bda329e Lazy load available languages 2020-05-18 16:21:47 +02:00
Benoit Marty
a00ddca188 Create SystemLocaleProvider 2020-05-18 16:21:47 +02:00
Benoit Marty
8ac2cb0530 Cleanup 2020-05-18 16:21:47 +02:00
Benoit Marty
19d655ec41 Locales: improve algo 2020-05-18 16:21:47 +02:00
Benoit Marty
4961bb0e08 FontSize: rework by creating FontScaleValue data class. 2020-05-18 16:21:47 +02:00
Benoit Marty
f45040c405 Locale: support locale change 2020-05-18 16:21:24 +02:00
Benoit Marty
9fed62e4a7 Improve template 2020-05-18 16:21:00 +02:00
Benoit Marty
43b9e2cec9 Merge pull request #1382 from vector-im/feature/aiplane_mode
Better connectivity lost indicator when airplane mode is on
2020-05-18 15:35:14 +02:00
Benoit Marty
3185b88fe5 Better connectivity lost indicator when airplane mode is on 2020-05-18 15:09:14 +02:00
LinAGKar
c2906d9b06 Translated using Weblate (Swedish)
Currently translated at 14.6% (245 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-16 18:28:48 +00:00
Амёба
cfb615f972 Translated using Weblate (Russian)
Currently translated at 82.1% (1381 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/ru/
2020-05-16 18:28:48 +00:00
Priit Jõerüüt
9c22c0952c Translated using Weblate (Estonian)
Currently translated at 5.9% (100 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/et/
2020-05-16 18:28:48 +00:00
Priit Jõerüüt
5a834619c0 Translated using Weblate (Estonian)
Currently translated at 97.5% (159 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/et/
2020-05-16 18:28:43 +00:00
LinAGKar
3b62f50f7b Translated using Weblate (Swedish)
Currently translated at 14.4% (242 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-16 08:03:01 +00:00
LinAGKar
18e804d174 Translated using Weblate (Swedish)
Currently translated at 14.3% (241 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-16 07:22:14 +00:00
Szimszon
c105d82027 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1682 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hu/
2020-05-16 07:22:11 +00:00
Kévin C
860921217d Translated using Weblate (French)
Currently translated at 100.0% (1682 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-16 07:22:06 +00:00
Priit Jõerüüt
3fe2f2876a Translated using Weblate (Estonian)
Currently translated at 1.6% (27 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/et/
2020-05-16 07:22:05 +00:00
Marko Dimjašević
e84fd408be Translated using Weblate (Croatian)
Currently translated at 0.5% (8 of 1682 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hr/
2020-05-16 07:22:05 +00:00
Marko Dimjašević
f361fd7355 Added translation using Weblate (Croatian) 2020-05-15 21:33:36 +00:00
ganfra
458e3ee5e8 Timeline: fetch next token with the help of getContext when required 2020-05-15 20:18:07 +02:00
Benoit Marty
5fa247a0c5 Remove temporary tool and strings_riotX.xml temporary files 2020-05-15 15:50:15 +02:00
Benoit Marty
48e58967b2 Version++ 2020-05-15 15:48:15 +02:00
Benoit Marty
d1d79c0191 Merge branch 'release/0.20.0' 2020-05-15 15:45:26 +02:00
Benoit Marty
0a41a9f773 Merge branch 'release/0.20.0' into develop 2020-05-15 15:45:26 +02:00
Benoit Marty
03fa0e6ad6 Prepare release 0.20.0 2020-05-15 15:44:36 +02:00
Benoit Marty
8eebcef4e9 Fix #1373 2020-05-15 15:36:52 +02:00
Benoit Marty
ea1c75c16a Fix lint issues in Strings 2020-05-15 12:58:14 +02:00
Benoit Marty
7a2aefd8fb Format string resources 2020-05-15 12:46:20 +02:00
Benoit Marty
33ec1bbfb3 Merge pull request #1371 from RiotTranslateBot/weblate-riot-android-riotx-application
Update from Weblate
2020-05-15 12:41:06 +02:00
Weblate
8883832b86 Merge branch 'origin/develop' into Weblate. 2020-05-15 10:09:23 +00:00
LinAGKar
308828ef50 Translated using Weblate (Swedish)
Currently translated at 14.3% (239 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-15 10:04:42 +00:00
Benoit Marty
885dac4ad1 Translated using Weblate (French)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-15 10:04:39 +00:00
Priit Jõerüüt
c03d61e09f Translated using Weblate (Estonian)
Currently translated at 1.5% (25 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/et/
2020-05-15 10:04:38 +00:00
Priit Jõerüüt
b2bacdfa4e Translated using Weblate (Estonian)
Currently translated at 92.6% (151 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/et/
2020-05-15 10:04:36 +00:00
Tirifto
06defaf14e Translated using Weblate (Esperanto)
Currently translated at 24.2% (404 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/eo/
2020-05-15 10:04:33 +00:00
Benoit Marty
4698cf7a9b Merge pull request #1367 from vector-im/feature/fix_crash_1366
Fix crash on restore backup from ky
2020-05-14 16:07:40 +02:00
Priit Jõerüüt
5004fba986 Added translation using Weblate (Estonian) 2020-05-14 08:16:34 +00:00
Priit Jõerüüt
8cc82fe5ba Added translation using Weblate (Estonian) 2020-05-14 08:13:31 +00:00
LinAGKar
c9fb231714 Translated using Weblate (Swedish)
Currently translated at 12.0% (200 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-13 23:28:56 +00:00
Амёба
0f22b55786 Translated using Weblate (Russian)
Currently translated at 82.5% (1380 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/ru/
2020-05-13 23:28:55 +00:00
zurtel22
535148e68a Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 23:28:47 +00:00
Dominik Mahnkopf
878e093b6b Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 23:28:47 +00:00
Tirifto
0e5f741b6b Translated using Weblate (Esperanto)
Currently translated at 21.2% (355 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/eo/
2020-05-13 23:28:47 +00:00
Dominik Mahnkopf
36b1717fc1 Translated using Weblate (Dutch)
Currently translated at 68.4% (1144 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/nl/
2020-05-13 23:28:38 +00:00
tleydxdy
37392b5495 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/zh_Hans/
2020-05-13 23:28:38 +00:00
nikonak
84f2fc41b3 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 20:46:48 +00:00
Dominik Mahnkopf
ebdf75091a Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 20:46:48 +00:00
nikonak
ce304ace2b Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 20:24:08 +00:00
Dominik Mahnkopf
0d2acec73e Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 20:24:08 +00:00
nikonak
0144764f69 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 19:34:47 +00:00
Dominik Mahnkopf
aad4b3dc39 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-13 19:34:47 +00:00
Valere
040deea655 Fix crash on restore backup from ky 2020-05-13 16:59:55 +02:00
Valere
1e2b5dd428 Merge pull request #1365 from vector-im/feature/fix_crash_1364
Fix crash 1364
2020-05-13 16:59:08 +02:00
Valere
8d32c27ce0 Fix crash 1364 2020-05-13 16:57:57 +02:00
Valere
074a9e9f29 Merge pull request #1338 from vector-im/feature/crash_manual_verify
Crashes when private key missing
2020-05-13 16:35:38 +02:00
Valere
650b6bd9ea Merge branch 'develop' into feature/crash_manual_verify 2020-05-13 16:35:28 +02:00
Benoit Marty
3dd74d6828 Merge pull request #1095 from vector-im/feature/wellknown
Add wellknown support in the login flow
2020-05-13 15:29:02 +02:00
Benoit Marty
f717a37a4a Split long line 2020-05-13 15:28:05 +02:00
Benoit Marty
d8b1372a0f Login request does not provide the full Wellknown data. Change the model to reflect that, to avoid misunderstanding. 2020-05-13 14:03:10 +02:00
Benoit Marty
678cf50dbd Add Javadoc 2020-05-13 13:56:33 +02:00
Benoit Marty
57fca80cbb Disable possibility to login using matrixId (waiting for design) 2020-05-13 13:33:12 +02:00
Benoit Marty
cf7de8bb8b Typo 2020-05-13 12:43:54 +02:00
Benoit Marty
a70fdedce5 Try to use wellKnown request, when user is entering a homeserver URL 2020-05-13 12:43:54 +02:00
Benoit Marty
c173235ee3 ktlint 2020-05-13 12:43:54 +02:00
Benoit Marty
f74b1e6c2e Migrate Login Navigation view model to regular ViewEvents 2020-05-13 12:43:54 +02:00
Benoit Marty
c9bc6f4a9e Support homeserver discovery from MXID - Wellknown (#476) 2020-05-13 12:42:08 +02:00
Benoit Marty
63c18e82c8 typo... 2020-05-13 00:34:33 +02:00
Benoit Marty
037b2e1d60 PR merged after a release, move 2 lines 2020-05-13 00:34:03 +02:00
Benoit Marty
aea9c958bf Merge pull request #1307 from vector-im/feature/invite_members_to_room
Invite members to an existing room
2020-05-13 00:29:55 +02:00
Benoit Marty
7f185a729e Merge pull request #1345 from emmaguy/direct-shortcuts
Add direct shortcuts
2020-05-13 00:25:58 +02:00
Emma Vanbrabant
d08b4e1ea0 PR feedback 2020-05-12 19:37:03 +01:00
onurays
4eaed945e2 Fix plurals. 2020-05-12 15:31:15 +03:00
HaHaNoname
14b1b10556 Translated using Weblate (Russian)
Currently translated at 75.2% (1258 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/ru/
2020-05-12 12:28:46 +00:00
Tirifto
df762e40bb Translated using Weblate (Esperanto)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/eo/
2020-05-12 12:28:46 +00:00
Tirifto
684972185f Translated using Weblate (Esperanto)
Currently translated at 14.7% (245 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/eo/
2020-05-12 12:28:45 +00:00
onurays
04dd13d03b Use plurals in case of 3 or more invited users. 2020-05-12 14:10:23 +03:00
onurays
700fd47f22 Toast message formatting of invited users. 2020-05-12 12:10:45 +03:00
Benoit Marty
b36759deb4 Merge pull request #1226 from ndarilek/develop
Set `tickerText` to improve accessibility of notifications.
2020-05-11 22:28:36 +02:00
Benoit Marty
25d224be6b Merge branch 'develop' into develop 2020-05-11 22:27:16 +02:00
onurays
fe013f803e Add action menu icon to invite users. 2020-05-11 22:43:55 +03:00
Benoit Marty
6abc51d05d Merge pull request #1339 from vector-im/feature/openId
Create a specific module for OpenId
2020-05-11 15:41:41 +02:00
LinAGKar
a98916c985 Translated using Weblate (Swedish)
Currently translated at 11.1% (186 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-11 10:28:38 +00:00
Frisk
9e29533aad Translated using Weblate (Polish)
Currently translated at 93.3% (1560 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/pl/
2020-05-11 10:28:37 +00:00
tctovsli
7119403cde Translated using Weblate (Norwegian Bokmål)
Currently translated at 34.5% (577 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/nb_NO/
2020-05-11 10:28:36 +00:00
nikonak
7f55e4fb1e Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-11 10:28:34 +00:00
zurtel22
82df62a600 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-11 10:28:34 +00:00
Pepper.Cabbit.Snoopy
d0c722eae1 Translated using Weblate (Chinese (Simplified))
Currently translated at 64.5% (1078 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/zh_Hans/
2020-05-11 10:28:33 +00:00
nikonak
40649e9c3c Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-10 22:23:53 +00:00
zurtel22
431b285806 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-10 22:23:52 +00:00
nikonak
75f84fe1f4 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-10 22:20:28 +00:00
zurtel22
98bf02efa9 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-10 22:20:28 +00:00
nikonak
0eb68b531c Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-10 21:47:22 +00:00
zurtel22
9124844e3e Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-10 21:47:22 +00:00
Emma Vanbrabant
f568553d21 Add to changes 2020-05-09 21:07:03 +01:00
Emma Vanbrabant
92c9d4fc22 remove unused import
Signed-off-by: Emma Vanbrabant <emmag87@gmail.com>
2020-05-09 20:58:15 +01:00
Emma Vanbrabant
957d51cf3f Fix placeholder icons 2020-05-09 20:50:58 +01:00
Emma Vanbrabant
54ecc25831 Create ShortcutBuilder and use 2020-05-09 20:12:51 +01:00
kujaw
738a368a6f Translated using Weblate (Polish)
Currently translated at 93.1% (1557 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/pl/
2020-05-09 18:02:25 +00:00
LinAGKar
969f070175 Translated using Weblate (Swedish)
Currently translated at 10.6% (178 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-09 16:28:51 +00:00
@a2sc:matrix.org
d8d78b124d Translated using Weblate (German)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-09 16:28:37 +00:00
Alexander Eisele
9a9f0c200e Translated using Weblate (German)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-09 16:28:37 +00:00
Tirifto
247ffc1270 Translated using Weblate (Esperanto)
Currently translated at 82.2% (134 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/eo/
2020-05-09 16:28:37 +00:00
LinAGKar
690d05aeca Translated using Weblate (Danish)
Currently translated at 23.8% (398 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/da/
2020-05-09 16:28:30 +00:00
LinAGKar
c33f3b76fa Translated using Weblate (Swedish)
Currently translated at 0.5% (8 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sv/
2020-05-08 06:28:32 +00:00
laeberkaes
8616c454e1 Translated using Weblate (German)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/de/
2020-05-08 06:28:31 +00:00
laeberkaes
17db994d35 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-08 06:28:30 +00:00
Samu Voutilainen
6c1c1ca8b0 Translated using Weblate (Finnish)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/fi/
2020-05-08 06:28:30 +00:00
Ville Ranki
f1613eacbb Translated using Weblate (Finnish)
Currently translated at 94.0% (1572 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fi/
2020-05-08 06:28:30 +00:00
Besnik Bleta
67d1c2dc80 Translated using Weblate (Albanian)
Currently translated at 98.8% (161 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/sq/
2020-05-08 06:28:28 +00:00
Besnik Bleta
c2b2b856a1 Translated using Weblate (Albanian)
Currently translated at 99.6% (1665 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sq/
2020-05-08 06:28:28 +00:00
LinAGKar
44f946513f Added translation using Weblate (Swedish) 2020-05-08 06:15:51 +00:00
Valere
1cafca6de6 Merge pull request #1334 from vector-im/feature/fix_1329
Fix #1329
2020-05-07 17:45:09 +02:00
Nolan Darilek
35ee7f0b40 Add changelog entry. 2020-05-07 15:38:28 +00:00
Nolan Darilek
6e8e7164c6 Use last event when generating ticker text. 2020-05-07 15:29:42 +00:00
Nolan Darilek
6bbded1e65 Use string resource for generating ticker text. 2020-05-07 15:14:31 +00:00
Ville Ranki
0aa90c3eea Translated using Weblate (Finnish)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/fi/
2020-05-07 06:15:43 +00:00
Alexander Eisele
b44b0ec998 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:10:26 +00:00
laeberkaes
07c6259734 Translated using Weblate (German)
Currently translated at 99.9% (1671 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:10:25 +00:00
Alexander Eisele
1785d4d0b4 Translated using Weblate (German)
Currently translated at 99.6% (1665 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:08:11 +00:00
laeberkaes
22d06928c8 Translated using Weblate (German)
Currently translated at 99.6% (1665 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:08:11 +00:00
Alexander Eisele
d6fe6e44bd Translated using Weblate (German)
Currently translated at 99.3% (1660 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:06:47 +00:00
laeberkaes
d51ee19f3f Translated using Weblate (German)
Currently translated at 99.3% (1660 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:06:47 +00:00
Alexander Eisele
717e5161a6 Translated using Weblate (German)
Currently translated at 98.9% (1654 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:05:07 +00:00
laeberkaes
be9fa268b1 Translated using Weblate (German)
Currently translated at 98.9% (1654 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:05:07 +00:00
Alexander Eisele
14e8bbcec6 Translated using Weblate (German)
Currently translated at 98.9% (1653 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:03:45 +00:00
laeberkaes
26105dc25f Translated using Weblate (German)
Currently translated at 98.9% (1653 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:03:45 +00:00
Alexander Eisele
53ba1c2068 Translated using Weblate (German)
Currently translated at 98.8% (1652 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:03:19 +00:00
laeberkaes
fa004c9d93 Translated using Weblate (German)
Currently translated at 98.8% (1652 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:03:18 +00:00
Alexander Eisele
be94921918 Translated using Weblate (German)
Currently translated at 98.7% (1650 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:00:54 +00:00
laeberkaes
2f5fe59aa6 Translated using Weblate (German)
Currently translated at 98.7% (1650 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 22:00:53 +00:00
Alexander Eisele
89fb2cf391 Translated using Weblate (German)
Currently translated at 98.3% (1644 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:55:58 +00:00
laeberkaes
f1d2abc9b1 Translated using Weblate (German)
Currently translated at 98.3% (1644 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:55:58 +00:00
Alexander Eisele
2aa8512f6f Translated using Weblate (German)
Currently translated at 98.1% (1641 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:52:02 +00:00
laeberkaes
2d31402cf0 Translated using Weblate (German)
Currently translated at 98.1% (1641 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:52:02 +00:00
Alexander Eisele
6d61848ed6 Translated using Weblate (German)
Currently translated at 98.1% (1640 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:42:56 +00:00
laeberkaes
0e0b724535 Translated using Weblate (German)
Currently translated at 98.1% (1640 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:42:55 +00:00
Alexander Eisele
e13915b0c7 Translated using Weblate (German)
Currently translated at 98.0% (1639 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:40:19 +00:00
laeberkaes
361f0415bb Translated using Weblate (German)
Currently translated at 98.0% (1639 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:40:18 +00:00
Alexander Eisele
c3b662fa1f Translated using Weblate (German)
Currently translated at 98.0% (1638 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:40:01 +00:00
laeberkaes
2d3e23ee11 Translated using Weblate (German)
Currently translated at 98.0% (1638 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:40:00 +00:00
Alexander Eisele
fc8ab0d462 Translated using Weblate (German)
Currently translated at 97.9% (1637 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:36:10 +00:00
laeberkaes
89629ffe93 Translated using Weblate (German)
Currently translated at 97.9% (1637 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:36:10 +00:00
Alexander Eisele
e97d565809 Translated using Weblate (German)
Currently translated at 97.8% (1636 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:35:28 +00:00
laeberkaes
7e3413eda7 Translated using Weblate (German)
Currently translated at 97.8% (1636 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:35:28 +00:00
Alexander Eisele
7966f6e308 Translated using Weblate (German)
Currently translated at 97.7% (1634 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:34:42 +00:00
laeberkaes
e1286a0ed4 Translated using Weblate (German)
Currently translated at 97.7% (1634 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:34:42 +00:00
Alexander Eisele
1e2d267fec Translated using Weblate (German)
Currently translated at 97.6% (1632 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:32:36 +00:00
laeberkaes
8b3403c115 Translated using Weblate (German)
Currently translated at 97.6% (1632 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:32:35 +00:00
Alexander Eisele
da4b029093 Translated using Weblate (German)
Currently translated at 97.5% (1631 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:32:22 +00:00
laeberkaes
b48113a353 Translated using Weblate (German)
Currently translated at 97.5% (1631 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:32:21 +00:00
Alexander Eisele
e1884e7c73 Translated using Weblate (German)
Currently translated at 97.5% (1630 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:32:01 +00:00
laeberkaes
daae030134 Translated using Weblate (German)
Currently translated at 97.5% (1630 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:32:01 +00:00
Alexander Eisele
19e1da1216 Translated using Weblate (German)
Currently translated at 97.3% (1627 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:30:27 +00:00
laeberkaes
e9bb95b3b3 Translated using Weblate (German)
Currently translated at 97.3% (1627 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:30:26 +00:00
Alexander Eisele
a43ca5925c Translated using Weblate (German)
Currently translated at 97.1% (1624 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:28:41 +00:00
laeberkaes
dd46798bda Translated using Weblate (German)
Currently translated at 97.1% (1624 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:28:41 +00:00
Alexander Eisele
d70a09ded8 Translated using Weblate (German)
Currently translated at 97.1% (1623 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:27:28 +00:00
laeberkaes
439aa7854c Translated using Weblate (German)
Currently translated at 97.1% (1623 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 21:27:28 +00:00
Benoit Marty
750550ad3e Create a specific module for OpenId 2020-05-06 22:14:56 +02:00
Besnik Bleta
b6af2269d2 Translated using Weblate (Albanian)
Currently translated at 99.6% (1665 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sq/
2020-05-06 18:59:23 +00:00
kujaw
afbda4ac28 Translated using Weblate (Polish)
Currently translated at 93.1% (1556 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/pl/
2020-05-06 17:23:33 +00:00
Szimszon
f5fd0ac323 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hu/
2020-05-06 17:23:31 +00:00
Alexander Eisele
18de0ca951 Translated using Weblate (German)
Currently translated at 96.9% (1621 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 17:23:31 +00:00
Christopher Rossbach
b53c073b90 Translated using Weblate (German)
Currently translated at 96.9% (1621 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-06 17:23:30 +00:00
Kévin C
13ebef334f Translated using Weblate (French)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-06 17:23:30 +00:00
Ville Ranki
b1ba4e393e Translated using Weblate (Finnish)
Currently translated at 93.8% (1568 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fi/
2020-05-06 17:23:30 +00:00
Martijn de Boer
d898bc71f7 Translated using Weblate (Dutch)
Currently translated at 68.4% (1144 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/nl/
2020-05-06 17:23:20 +00:00
Valere
0afa7a706a Update change log 2020-05-06 18:20:43 +02:00
Valere
da68212255 Crashes when private key missing 2020-05-06 18:14:44 +02:00
Valere
583139d51e klint 2020-05-06 15:06:34 +02:00
Valere
cee8ae3af4 Fix #1329
+ migration to remove duplicate
2020-05-06 15:04:17 +02:00
onurays
c7c6cf70e4 Code review fixes. 2020-05-06 11:20:08 +03:00
Dirmin
1491bddb3b Translated using Weblate (French)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-06 06:42:49 +00:00
Alexander Eisele
ac5db83880 Translated using Weblate (German)
Currently translated at 96.5% (1614 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-05 13:35:17 +00:00
Christopher Rossbach
b2f3ba220e Translated using Weblate (German)
Currently translated at 96.5% (1614 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-05 13:35:17 +00:00
n3niu
deb783f797 Translated using Weblate (German)
Currently translated at 96.5% (1613 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-05 13:34:25 +00:00
Alexander Eisele
5fcf54cd57 Translated using Weblate (German)
Currently translated at 96.5% (1613 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-05 13:34:25 +00:00
Benoit Marty
4306cb7812 Upgrade build tools version (SDK 29) 2020-05-05 12:08:19 +02:00
Benoit Marty
a4b8dc9400 Fix test compilation issue 2020-05-05 11:49:03 +02:00
Benoit Marty
be9393fabe Merge branch 'hotfix/fix_crash_before_release' 2020-05-05 11:33:40 +02:00
Benoit Marty
c7a7ad7b57 Merge branch 'hotfix/fix_crash_before_release' into develop 2020-05-05 11:33:40 +02:00
Valere
78b7f03138 Fix / Sending events have warning until encrypted 2020-05-05 10:15:42 +02:00
Benoit Marty
ffeae7ec83 Fix timeline navigation when opening an event in a previous lastForward chunk.
In this case, we do not have a nextToken, but there are more event to load. So we perform a GET /context on the last known event.
Not sure it is correct to do that though...
2020-05-05 02:41:32 +02:00
Benoit Marty
db77e7b817 Create a fun 2020-05-05 02:41:32 +02:00
Benoit Marty
fcee85a682 Cleanup and doc 2020-05-05 02:41:32 +02:00
Benoit Marty
17ddb5ce43 if all events are rendered in the timeline (developer mode), render the room creation event. 2020-05-05 02:41:32 +02:00
Benoit Marty
53583c691f Add some logs 2020-05-05 02:41:32 +02:00
Benoit Marty
2b9d3960b3 Improve tests 2020-05-05 02:41:32 +02:00
Benoit Marty
92befcde5d Add test to cover previous last forward case (passing) 2020-05-05 02:41:32 +02:00
Benoit Marty
697eaec197 TI: After jump to unread, newer messages are never loaded (#1008) 2020-05-05 02:41:32 +02:00
Benoit Marty
86fba28313 After jump to unread, newer messages are never loaded (#1008) 2020-05-05 02:41:32 +02:00
Benoit Marty
f3c3c07d46 Kotlin sugar 2020-05-05 00:14:57 +02:00
Benoit Marty
8966e24925 Create a debug method to send x times the same event 2020-05-05 00:14:57 +02:00
Benoit Marty
becc5a7b54 Add assertion in debug 2020-05-05 00:14:57 +02:00
Benoit Marty
a61434ae08 doc 2020-05-05 00:14:57 +02:00
Benoit Marty
20b726819f Rename "LastLive" -> "LastForward" 2020-05-05 00:14:57 +02:00
Benoit Marty
bfd847179f Wait more 2020-05-05 00:14:57 +02:00
Benoit Marty
7e955ef0e4 Add possibility to create clear room 2020-05-05 00:14:57 +02:00
Benoit Marty
2697800deb Doc and cleanup 2020-05-05 00:14:57 +02:00
Benoit Marty
2c47fe9f0d typo 2020-05-05 00:14:57 +02:00
aWeinzierl
423f21b02e Translated using Weblate (German)
Currently translated at 96.4% (1611 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-04 18:43:34 +00:00
Alexander Eisele
eb6546d81c Translated using Weblate (German)
Currently translated at 96.4% (1611 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-04 18:43:33 +00:00
upgradetofreedom
4e2878300f Translated using Weblate (German)
Currently translated at 96.4% (1611 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-04 18:43:33 +00:00
Valere
4578b9df7f Fix / froezn object migration 2020-05-04 18:27:38 +02:00
Benoit Marty
d679c9d5d8 Cleanup 2020-05-04 17:30:55 +02:00
Benoit Marty
dc7b3dfc9d Fix crash when entering wrong passphrase 2020-05-04 17:30:13 +02:00
Benoit Marty
6843ea113b Version++ 2020-05-04 16:06:41 +02:00
Benoit Marty
0a8c954397 Merge remote-tracking branch 'origin/master' 2020-05-04 16:04:51 +02:00
Benoit Marty
358e10a093 Merge branch 'release/0.19.0' 2020-05-04 16:03:35 +02:00
Benoit Marty
c0b7ea6dd1 Merge branch 'release/0.19.0' into develop 2020-05-04 16:03:34 +02:00
Benoit Marty
0b5e618c1c Update CHANGES.md 2020-05-04 16:03:01 +02:00
Benoit Marty
1f528ee428 Format strings 2020-05-04 16:00:11 +02:00
Benoit Marty
bbd8b89589 Merge pull request #1321 from RiotTranslateBot/weblate-riot-android-riotx-application
Update from Weblate
2020-05-04 15:52:06 +02:00
Andrej Shadura
a5d2d65131 Translated using Weblate (Slovak)
Currently translated at 54.5% (912 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sk/
2020-05-04 11:50:58 +00:00
Szimszon
b45504d97a Translated using Weblate (Hungarian)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/hu/
2020-05-04 11:50:57 +00:00
Szimszon
0598ecaca3 Translated using Weblate (Hungarian)
Currently translated at 99.8% (1669 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/hu/
2020-05-04 11:50:57 +00:00
Alexander Eisele
0d9749a515 Translated using Weblate (German)
Currently translated at 95.9% (1604 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-04 11:50:55 +00:00
HayWo
836766f978 Translated using Weblate (German)
Currently translated at 95.9% (1604 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-04 11:50:55 +00:00
code-surfer
93851d0ab2 Translated using Weblate (German)
Currently translated at 95.9% (1604 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-04 11:50:55 +00:00
Ville Ranki
5fff637bee Translated using Weblate (Finnish)
Currently translated at 99.4% (162 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/fi/
2020-05-04 11:50:55 +00:00
Ville Ranki
eae015caa1 Translated using Weblate (Finnish)
Currently translated at 86.0% (1438 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fi/
2020-05-04 11:50:54 +00:00
David
80a356c7e2 Translated using Weblate (Czech)
Currently translated at 98.4% (1645 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/cs/
2020-05-04 11:50:52 +00:00
onurays
3a0eed795a Lint fix. 2020-05-04 12:09:36 +03:00
onurays
c1c0c6f2c6 Lint fixes. 2020-05-04 11:22:27 +03:00
code-surfer
f04868ba19 Translated using Weblate (German)
Currently translated at 95.8% (1601 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-03 14:14:24 +00:00
n3niu
b052884912 Translated using Weblate (German)
Currently translated at 95.8% (1601 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-03 14:14:24 +00:00
code-surfer
7fb7729af6 Translated using Weblate (German)
Currently translated at 95.8% (1601 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-03 14:14:24 +00:00
random
2f5d824c65 Translated using Weblate (Italian)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/it/
2020-05-03 14:14:24 +00:00
random
fbc46b3c8b Translated using Weblate (Italian)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/it/
2020-05-03 14:14:24 +00:00
n3niu
e986c9d343 Translated using Weblate (German)
Currently translated at 95.6% (1599 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/de/
2020-05-03 14:14:24 +00:00
Kévin C
3100473305 Translated using Weblate (French)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/fr/
2020-05-03 14:14:24 +00:00
Kévin C
5eb9f32acb Translated using Weblate (French)
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-03 14:14:24 +00:00
Jeff Huang
0d12a80832 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/zh_Hant/
2020-05-03 14:14:24 +00:00
Jeff Huang
077c166c09 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1672 of 1672 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/zh_Hant/
2020-05-03 14:14:24 +00:00
Akarshan Biswas
5d26b6a7cb Translated using Weblate (Bengali (India))
Currently translated at 12.3% (20 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/bn_IN/
2020-05-03 14:14:24 +00:00
Akarshan Biswas
68c1e8fc6d Added translation using Weblate (Bengali (India)) 2020-05-03 14:14:24 +00:00
yuuki-san
1ffd7dbb9f Translated using Weblate (Slovak)
Currently translated at 92.6% (151 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/sk/
2020-05-03 14:14:24 +00:00
yuuki-san
0cc48a190f Translated using Weblate (Slovak)
Currently translated at 54.2% (904 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sk/
2020-05-03 14:14:24 +00:00
random
8206a78156 Translated using Weblate (Italian)
Currently translated at 98.7% (1645 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/it/
2020-05-03 14:14:24 +00:00
Jeff Huang
6a1e38ca04 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1667 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/zh_Hant/
2020-05-03 14:14:24 +00:00
Slavi Pantaleev
779f380d2f Translated using Weblate (Bulgarian)
Currently translated at 100.0% (163 of 163 strings)

Translation: Riot Android/RiotX Matrix SDK
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-matrix-sdk/bg/
2020-05-03 14:14:24 +00:00
rkfg
55f7461747 Translated using Weblate (Russian)
Currently translated at 75.4% (1257 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/ru/
2020-05-03 14:14:24 +00:00
Kévin C
7665aba22c Translated using Weblate (French)
Currently translated at 100.0% (1667 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/fr/
2020-05-03 14:14:24 +00:00
Jeff Huang
e96c5f7305 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (1667 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/zh_Hant/
2020-05-03 14:14:24 +00:00
yuuki-san
c5ba34d619 Translated using Weblate (Slovak)
Currently translated at 54.0% (901 of 1667 strings)

Translation: Riot Android/RiotX application
Translate-URL: https://translate.riot.im/projects/riot-android/riotx-application/sk/
2020-05-03 14:14:24 +00:00
Valere
c13439eeb9 Merge pull request #1317 from vector-im/feature/fix_crash_bootstrap
Fix / Crash on bootstrap
2020-05-03 11:59:32 +02:00
Valere
d27b73f6be Fix / Crash on bootstrap
Exception: java.lang.IllegalArgumentException: 'value' is not a valid managed object.
2020-05-03 11:36:40 +02:00
Benoit Marty
bb427700d2 Merge pull request #1310 from vector-im/feature/room_creation
Several fixes on room creation collpasing events (Fixes #1309)
2020-04-30 18:19:49 +02:00
Valere
4589aaa11c Merge pull request #1312 from vector-im/feature/post_smoke_test_fix
Feature/post smoke test fix
2020-04-30 17:37:04 +02:00
Valere
b3dbcd7936 Show untrusted first 2020-04-30 15:43:19 +02:00
Valere
cac246aa15 update copy 2020-04-30 15:43:05 +02:00
Onuray Sahin
d2f0957eba Merge branch 'develop' into feature/invite_members_to_room 2020-04-30 16:11:52 +03:00
onurays
db18272ef2 Remove strings from strings_riotX.xml 2020-04-30 15:47:31 +03:00
onurays
cf5d89ea9b Documentation added for new parameter excludedUserIds. 2020-04-30 15:40:54 +03:00
onurays
0aeb327062 Changelog added. 2020-04-30 15:40:02 +03:00
onurays
5dc50195b3 Filter existing room members. 2020-04-30 15:28:20 +03:00
Benoit Marty
83db9b34d4 Merge pull request #1311 from vector-im/bmarty-patch-1
Add instruction regarding the template
2020-04-30 13:50:44 +02:00
Benoit Marty
a43df43642 Add instruction regarding the template 2020-04-30 13:10:53 +02:00
onurays
57a87ba620 Add InviteUsersToRoomActivity and mvrx classes. 2020-04-30 13:54:09 +03:00
Benoit Marty
f6cbc15cf7 Several fixes on room creation collpasing events (Fixes #1309)
- do not collapse room member events
- collapse other type of event: topic, alias, canonical alias, powel level
- Use correct user name for collapsed version (should be fixed twice due to the previous change of excluding some room member events)
- align "join" and "left" string with Riot-Web
2020-04-30 12:52:21 +02:00
Benoit Marty
7322144dc8 Remove duplicated strings 2020-04-30 12:15:26 +02:00
Valere
8e357c6b7f Merge pull request #1280 from vector-im/feature/e2e_timeline_decoration
Feature/e2e timeline decoration
2020-04-30 12:01:55 +02:00
Valere
7b20db64a5 Merge branch 'develop' into feature/e2e_timeline_decoration 2020-04-30 12:01:44 +02:00
Valere
429c634ed9 Merge pull request #1308 from vector-im/feature/fix_dm_shield_logic
Fix / Move DM shield rules to task
2020-04-30 12:00:18 +02:00
Valere
05230a6afa Code review 2020-04-30 11:38:32 +02:00
Valere
43eb804b23 Merge pull request #1303 from vector-im/feature/xs_old_new_session_detection
Feature/xs old new session detection
2020-04-30 11:23:15 +02:00
Valere
5840248ffa Fix / NPE Optional#get instead of getOrNull 2020-04-30 11:11:11 +02:00
Valere
6ea38c7eb0 Fix / Move DM shield rules to task 2020-04-30 10:55:25 +02:00
Valere
9586fa9f90 Typo in file name 2020-04-30 10:11:32 +02:00
Valere
0d0af6906e Code review 2020-04-30 10:10:56 +02:00
Valere
93070f3524 Fix / use distinct until change 2020-04-30 09:50:47 +02:00
Valere
7cf7b7e10e Fix / avoid showing legacy start toaster under verif bottomsheet 2020-04-30 09:50:38 +02:00
Valere
1de4869cde Fix / reuse cell keeps icon 2020-04-30 09:50:11 +02:00
onurays
a4eba653a3 Make a generic user directory search & selection views. 2020-04-30 02:50:30 +03:00
ganfra
21d0db8382 Merge pull request #1304 from vector-im/feature/template
Feature/template
2020-04-29 19:21:01 +02:00
ganfra
269d6e4d08 Remove AndroidManifest template 2020-04-29 19:19:50 +02:00
Valere
3cf341c3bf code quality 2020-04-29 18:48:26 +02:00
Valere
f0a9be2ec7 Better session detection 2020-04-29 18:46:36 +02:00
Valere
8955e5461c Add retry to sendToDeviceTask 2020-04-29 18:45:51 +02:00
Valere
087ff1c041 Fix / race when receive accept in sending start in to device 2020-04-29 18:44:25 +02:00
ganfra
1a307a0c4d Template: let the ViewModel factory be agnostic of the host 2020-04-29 17:58:54 +02:00
Benoit Marty
071a43c8d4 Merge pull request #1305 from vector-im/feature/fix_delay_initial_sync
Fix / ensure ux aware of wait
2020-04-29 17:12:58 +02:00
Valere
7b46c49ded Fix / missing primary key for migration 2020-04-29 16:35:50 +02:00
Valere
da5672d229 Fix / ensure ux aware of wait 2020-04-29 16:18:01 +02:00
Valere
dcfd9ee7a7 Fix copy 2020-04-29 15:12:07 +02:00
ganfra
35a6f90ed6 Create configure script for template 2020-04-29 14:41:45 +02:00
ganfra
d463e5e500 Create template 2020-04-29 14:38:01 +02:00
Valere
0f00597444 Fix / Regression on non e2e device
+ migrate to new rx objects
2020-04-29 12:35:22 +02:00
Valere
a806f70b35 New security alert to review old sessions 2020-04-29 12:04:59 +02:00
Benoit Marty
67f07bd1bb Merge pull request #1297 from vector-im/feature/xsigning_trust_optimization
Feature/xsigning trust optimization
2020-04-29 10:32:29 +02:00
Valere
39e18446ae fix typo 2020-04-29 09:34:28 +02:00
Benoit Marty
4dc0b00569 Import string from Matrix SDK 2020-04-28 23:54:35 +02:00
Benoit Marty
87979ccadd Merge pull request #1299 from vector-im/feature/emoji_perf
Emoji completion for 🎉
2020-04-28 23:41:02 +02:00
Benoit Marty
7c2a5af8f2 Merge pull request #1301 from vector-im/feature/strings
Feature/strings
2020-04-28 23:40:39 +02:00
Benoit Marty
dc6d4c6789 Remove problematic translation 2020-04-28 21:54:14 +02:00
Benoit Marty
db3d5e2677 Remove not used anymore translations 2020-04-28 21:36:34 +02:00
Benoit Marty
6dc8bdde04 Import translation from Riot-Android 2020-04-28 21:35:39 +02:00
Valere
c02cfb2f4f Merge pull request #1296 from vector-im/feature/untrusted_session_shields
Update manage sessions screen
2020-04-28 19:17:00 +02:00
Valere
947c46d7b5 Avoid negative margin 2020-04-28 19:16:29 +02:00
ganfra
8942ce964a Fix android test not compiling 2020-04-28 19:09:20 +02:00
Valere
a05c401892 Code review 2020-04-28 18:47:54 +02:00
onurays
f25c981173 Add menu item to invite users to the room. 2020-04-28 17:30:23 +03:00
ganfra
43055964ba Crypto store : avoid copying before mapping to other data 2020-04-28 16:26:04 +02:00
Benoit Marty
a4192a0761 Emoji completion 🎉 does not completes to 🎉 like on web (#1285) 2020-04-28 14:29:43 +02:00
Benoit Marty
9c8ff7de7f Add Android test for EmojiDataSource 2020-04-28 14:15:50 +02:00
Benoit Marty
b4247c89e4 Make fun internal 2020-04-28 14:15:50 +02:00
Valere
cdabca6def Fix copy 2020-04-28 13:20:30 +02:00
Valere
2d6f0205a4 Update manage sessions screen 2020-04-28 13:20:30 +02:00
ganfra
4e8177f738 Fix lint 2020-04-28 13:10:44 +02:00
Valere
798e9e4fde Merge pull request #1287 from vector-im/feature/improve_security_toaster
Remember ignored unknown sessions
2020-04-28 12:38:14 +02:00
Valere
8871390167 Code review 2020-04-28 12:25:50 +02:00
ganfra
fc86e7e1f6 ShieldTrust: use only active members 2020-04-28 11:00:41 +02:00
ganfra
21912c290a XSigning keys: use json instead of object serialization 2020-04-28 10:59:51 +02:00
Benoit Marty
df335c7aa3 Merge pull request #1290 from vector-im/feature/cleanup_ui_state
Clear preferences when user logs out
2020-04-28 10:54:48 +02:00
Benoit Marty
8bd4cc8f54 Merge pull request #1277 from vector-im/feature/sso
Use correct sso url
2020-04-27 14:36:37 +02:00
Benoit Marty
4e3df99e42 Merge pull request #1281 from vector-im/feature/various_issues_verification_ssss_bootstrap
Feature/various issues verification ssss bootstrap
2020-04-27 14:32:18 +02:00
Benoit Marty
b1e1b4a7dc Remove "Reset keys" developer action 2020-04-27 14:25:45 +02:00
Benoit Marty
a233e9b0a0 Avoid code duplication, and improve readability 2020-04-27 14:25:45 +02:00
Benoit Marty
ebecb9bb9a i18n 2020-04-27 14:25:07 +02:00
Benoit Marty
35962c3cb5 Do not propose bootsrap for SSO accounts
Because we do not support yet confirming account credentials using SSO
2020-04-27 14:25:07 +02:00
Benoit Marty
0ac6a26b6e Add "continue" button to the bootstrap bottom sheet 2020-04-27 14:25:07 +02:00
Benoit Marty
0a887c0926 Cleanup 2020-04-27 14:25:07 +02:00
Benoit Marty
54c0239969 fix layout issue when text is displayed on 2 lines 2020-04-27 14:25:07 +02:00
Benoit Marty
8559254593 Merge pull request #1289 from vector-im/feature/fix-edited_event_click
Do not handle url if it is not valid.
2020-04-27 14:24:25 +02:00
onurays
626eb4d06b Comments added to explain why we should check if the url is valid. 2020-04-27 13:44:16 +03:00
Benoit Marty
aa16ba88ae Add hint to translators 2020-04-27 12:41:47 +02:00
Benoit Marty
a633c11c1d Do not clear developer preference when logging out 2020-04-27 12:29:38 +02:00
Benoit Marty
a4931e21ae Clear sharedPreference when logging out 2020-04-27 12:26:19 +02:00
Valere
996fabb327 Merge pull request #1288 from vector-im/feature/fix_update_4s_backup_after_bootstrap
Fix / backup key was not save in 4S after bootstrap
2020-04-27 11:42:09 +02:00
onurays
6c4e71d7d4 Do not handle url if it is not valid. 2020-04-27 12:26:35 +03:00
Valere
ad0ad502aa Fix / backup key was not save in 4S after bootstrap 2020-04-27 11:14:13 +02:00
Valere
42b47c25aa Remember ignored unknown sessions 2020-04-27 10:09:37 +02:00
Benoit Marty
7ef1970a0b Better layout preview 2020-04-27 01:17:20 +02:00
Benoit Marty
409d751612 Merge pull request #1278 from vector-im/feature/fix_misleading_url_color
Fix the color of misleading url according to design document.
2020-04-27 00:50:03 +02:00
Valere
bdce71abfd Update change log 2020-04-24 16:50:56 +02:00
Valere
114bce5f64 Fix / DB crash due to deserializaion 2020-04-24 16:50:56 +02:00
Valere
20e5ebc88b Decorate timeline with e2e warning 2020-04-24 16:50:56 +02:00
onurays
52aa57ac7c Fix the color of misleading url according to design document. 2020-04-24 17:18:59 +03:00
Benoit Marty
8daf72a4b0 Use correct URL for SSO connection (#1178) 2020-04-24 15:54:02 +02:00
Benoit Marty
51eb2cda95 Move some constants to the Matrix SDK 2020-04-24 15:53:30 +02:00
Benoit Marty
57779c99c2 improve script 2020-04-24 14:39:55 +02:00
Benoit Marty
02e02ed691 Merge pull request #1275 from vector-im/feature/log_improvement
Log improvement for test
2020-04-24 14:38:28 +02:00
Benoit Marty
af0b798ef1 Ensure Timber log output when running tests
to squash
2020-04-24 13:38:28 +02:00
Benoit Marty
51be8d5ed5 Remove previous temporary solution 2020-04-24 13:26:25 +02:00
Benoit Marty
270bed5013 EventBus logs using Timber 2020-04-24 11:57:49 +02:00
Benoit Marty
20b3c33fb0 Remove bad comment 2020-04-24 11:57:49 +02:00
Benoit Marty
b2aaf1cca1 CurlLoggingInterceptor now uses Timber to log 2020-04-24 11:57:49 +02:00
Onuray Sahin
5f6969e2cc Merge pull request #1270 from vector-im/feature/misleading_url_target
Show a warning dialog if the text of the clicked link does not match
2020-04-24 11:53:16 +03:00
Onuray Sahin
f0648ee52a Merge branch 'develop' into feature/misleading_url_target 2020-04-24 11:24:39 +03:00
Valere
88c70a2c10 Merge pull request #1266 from vector-im/feature/update_ssss_activity
Feature/update ssss activity
2020-04-23 21:20:43 +02:00
Valere
22c3ed6bb9 Code review 2020-04-23 21:20:01 +02:00
Valere
b0d25fa84f Update vector/src/main/res/values/strings_riotX.xml
Co-Authored-By: Benoit Marty <benoitm@matrix.org>
2020-04-23 21:16:46 +02:00
Valere
57636207d2 Fix copy 2020-04-23 21:16:46 +02:00
Valere
eac9133bb1 update change log 2020-04-23 21:16:46 +02:00
Valere
f7e7659750 klint 2020-04-23 21:16:28 +02:00
Valere
e719541b5e Fix / crash when generating random key 2020-04-23 21:16:28 +02:00
Valere
bd7acfbb1a Add option to recover with recovery key 2020-04-23 21:16:28 +02:00
Valere
25b42cb4f3 Merge pull request #1272 from vector-im/feature/update_design_complete_secu
Update design wait for self verification
2020-04-23 21:14:02 +02:00
Valere
928149fe35 Merge branch 'develop' into feature/update_design_complete_secu 2020-04-23 21:13:53 +02:00
Onuray Sahin
a80181da9e Merge branch 'develop' into feature/misleading_url_target 2020-04-23 20:18:44 +03:00
onurays
72de5d6adc Code review fixes. 2020-04-23 20:17:52 +03:00
Benoit Marty
ed4154d763 Merge pull request #1261 from vector-im/feature/unwedging
Feature/unwedging
2020-04-23 18:20:31 +02:00
Benoit Marty
4ee13b6fa1 Merge branch 'develop' into feature/unwedging 2020-04-23 18:20:09 +02:00
Benoit Marty
33fb1dd147 Merge pull request #1262 from vector-im/feature/fix_add_by_user_id
Add user to direct chat by user id.
2020-04-23 18:05:15 +02:00
Valere
736905edf8 Merge pull request #1269 from vector-im/feature/complete_security_hide_4s
Hide Use recovery key when 4S is not setup
2020-04-23 18:03:14 +02:00
Benoit Marty
e8a91eab88 Merge pull request #1265 from vector-im/feature/deactivate
Deactivate account using password
2020-04-23 17:30:08 +02:00
Valere
b951af0116 RiotX spelling 2020-04-23 17:00:37 +02:00
onurays
c3299845c1 use generic cancel and continue strings. 2020-04-23 17:44:30 +03:00
onurays
54644db587 Dialog design fixes. 2020-04-23 17:37:30 +03:00
Valere
cb0e93c43e use theme notice color 2020-04-23 16:23:36 +02:00
Valere
4c4ec6cfe8 Code review accessibility 2020-04-23 16:13:19 +02:00
Valere
449be02f53 update icon tint 2020-04-23 16:05:43 +02:00
Valere
25d2c2e2c6 Update design wait for self verification 2020-04-23 15:45:21 +02:00
onurays
ec2ba7c0b2 Do not warn if the domain of urls are the same and colorize links. 2020-04-23 16:30:37 +03:00
onurays
06a13d5c20 Show a warning dialog if the text of the clicked link does not match the link target
Fixes #922
2020-04-23 15:42:57 +03:00
Valere
7e0591ffee Hide Use recovery key when 4S is not setup 2020-04-23 11:14:20 +02:00
Benoit Marty
1363100f94 Create DM: now any userId can be entered, so deal with the case of the userId does not exists.
Use same string resource value than Riot-Web
2020-04-22 23:03:04 +02:00
Benoit Marty
06cf59bca7 Even if it's not happening, do not add the search term if already present in the results. 2020-04-22 19:20:13 +02:00
Valere
e37dd547b8 code review 2020-04-22 18:50:59 +02:00
Benoit Marty
671c1259af Merge pull request #1239 from waylon531/develop
Use a bigger thread pool
2020-04-22 18:23:37 +02:00
Benoit Marty
3d07ccd98e auto-review. Password could be only spaces... 2020-04-22 18:03:47 +02:00
Benoit Marty
03b9774c56 ktlint 2020-04-22 17:56:13 +02:00
Benoit Marty
0f1ddee71c Use SwitchCompat 2020-04-22 17:54:25 +02:00
Benoit Marty
855efa93cc Remove cancel button, useless. 2020-04-22 17:29:34 +02:00
Valere
d0f776a9cf Discard session command only encrypted room 2020-04-22 16:41:34 +02:00
Benoit Marty
da66e38c68 Close drawer when opening settings 2020-04-22 16:02:06 +02:00
Benoit Marty
a4ba8c152d Add IME action to the password field 2020-04-22 15:51:34 +02:00
Valere
9b320ed3c7 Fix unwedging 2020-04-22 15:40:59 +02:00
Benoit Marty
c854491248 Be more robust 2020-04-22 15:00:04 +02:00
Benoit Marty
5755d5bfaa Deactivate account: unit test and cleanup 2020-04-22 14:36:01 +02:00
Benoit Marty
ff320fec55 Move internal class to internal package 2020-04-21 20:47:49 +02:00
Benoit Marty
8c8a84b039 Account deactivation: the task does the cleanup 2020-04-21 20:41:10 +02:00
Benoit Marty
045e3d7bae Account deactivation (with password only) (#35) 2020-04-21 20:31:54 +02:00
Valere
491f0e6032 Merge pull request #1259 from vector-im/feature/restore_backup_from_ssss
KeyBackup / Use 4S if key in quadS
2020-04-21 14:31:26 +02:00
onurays
3163bc8b80 Add user to direct chat by user id.
Fixes #1065
2020-04-21 15:25:48 +03:00
Benoit Marty
eca3bf0817 typo 2020-04-21 13:49:36 +02:00
Valere
63355ca256 code review 2020-04-21 11:06:56 +02:00
Benoit Marty
c39a0e4fd5 timout -> timeout 2020-04-21 00:29:44 +02:00
Benoit Marty
59280ed18e Small improvement in documentation 2020-04-21 00:29:02 +02:00
Benoit Marty
c1acb1af66 Add integration test for change password feature 2020-04-21 00:23:01 +02:00
Valere
8a4f0a0c00 KeyBackup / Use 4S if key in quadS 2020-04-20 19:21:44 +02:00
Benoit Marty
a6368c473e Restart broken Olm sessions ([MSC1719](https://github.com/matrix-org/matrix-doc/pull/1719)) 2020-04-20 18:07:14 +02:00
Benoit Marty
3615ca6b95 VersionName can be null when running integration test 2020-04-20 18:07:14 +02:00
Benoit Marty
ddb00ba23a Enable Timber log in integration tests 2020-04-20 18:07:14 +02:00
Benoit Marty
91cf4b647d var -> val 2020-04-20 18:07:14 +02:00
Benoit Marty
f989eed8b0 Use @Throws(MXCryptoError::class) 2020-04-20 18:07:14 +02:00
Benoit Marty
4d296ddc09 Avoid injecting credentials 2020-04-20 18:07:14 +02:00
Benoit Marty
6186c22e02 improve code 2020-04-20 18:07:14 +02:00
Benoit Marty
13cd13a42f Create RoomEncryptorsStore 2020-04-20 18:07:14 +02:00
Benoit Marty
a42eb42178 Avoid injecting Credentials 2020-04-20 18:07:14 +02:00
Benoit Marty
7924ef207c Add Javadoc 2020-04-20 18:07:14 +02:00
Benoit Marty
5900245018 Make the test fail before unwedging implementation 2020-04-20 18:07:14 +02:00
Benoit Marty
00c239bc42 cleanup 2020-04-20 18:07:14 +02:00
Benoit Marty
0cb43eef51 Add test for Unwedging (before implementing it) 2020-04-20 18:07:14 +02:00
Benoit Marty
41a8f40241 Improve API 2020-04-20 18:07:14 +02:00
Benoit Marty
a8641ef879 Split KeysBackup to several files. No other change. 2020-04-20 18:07:14 +02:00
Benoit Marty
4d207e6acd Merge pull request #1258 from vector-im/feature/strings
Strings
2020-04-20 17:41:50 +02:00
Valere
1227de3f9c Merge pull request #1256 from vector-im/feature/increase_default_timeout
Increase default timeout to match old riot
2020-04-20 17:13:59 +02:00
Benoit Marty
6cad129625 Still waiting for translation update 2020-04-20 16:13:16 +02:00
Benoit Marty
9ccf51fbc0 Import string from Riot - new strings 2020-04-20 15:20:08 +02:00
Benoit Marty
990867204e Import string from Riot 2020-04-20 15:17:14 +02:00
Onuray Sahin
5795b7e063 Merge pull request #1242 from vector-im/feature/save_media_to_gallery
Save media files to Gallery
2020-04-20 13:24:33 +03:00
Valere
b612a7e63c Merge pull request #1252 from vector-im/feature/fix_device_detection_debounce
Avoid unnecessary /device call
2020-04-20 12:07:17 +02:00
Valere
50c73b68aa cleaning 2020-04-20 11:31:42 +02:00
Valere
c7ac5e2293 Increase default timeout to match old riot 2020-04-20 11:26:03 +02:00
Valere
43cb1fe68b Code review 2020-04-20 11:12:35 +02:00
onurays
f807de9a83 Lint fix. 2020-04-20 11:53:33 +03:00
Onuray Sahin
754f220596 Merge branch 'develop' into feature/save_media_to_gallery 2020-04-20 11:21:14 +03:00
onurays
3f7ca8669a Remove duplicated save icon. 2020-04-20 00:48:52 +03:00
Valere
28c6921a0a Avoid unnecessary /device call 2020-04-17 18:08:29 +02:00
onurays
26bb8ce2be Give user a toast after adding media file to the gallery. 2020-04-17 16:53:40 +03:00
onurays
8434f9326e Save action added to bottom sheet. 2020-04-17 15:27:42 +03:00
Waylon Cude
68f93c6c31 Merge branch 'develop' of https://github.com/vector-im/riotX-android into develop
Signed-off-by: Waylon Cude <waylon.cude@finzdani.net>
2020-04-16 12:19:17 -07:00
Benoit Marty
7961423556 Merge pull request #1244 from vector-im/feature/media_path
Fix download and upload media path
2020-04-16 17:58:08 +02:00
Benoit Marty
ac07fb47d7 Better Kotlin code 2020-04-16 17:42:55 +02:00
Valere
fbcbd6def5 Merge pull request #1243 from vector-im/feature/update_x_signing_copy
Feature/update x signing copy
2020-04-16 17:30:11 +02:00
Valere
3fe15f2d45 code review 2020-04-16 17:02:01 +02:00
Valere
968377a5be Update vector/src/main/res/values/strings_riotX.xml
Co-Authored-By: Benoit Marty <benoitm@matrix.org>
2020-04-16 16:53:24 +02:00
Benoit Marty
5652140f5d Improve ContentUrlResolver 2020-04-16 16:41:45 +02:00
Benoit Marty
e97c95f40a Fix issue with media path (Fixes #1227) 2020-04-16 16:41:45 +02:00
Valere
7ac5f58f32 Show CrossSigning info even if not developer mode 2020-04-16 16:30:45 +02:00
Valere
ce2f4e163d Update password/recovery copy + update icons 2020-04-16 16:30:45 +02:00
Benoit Marty
cc94b6cf7d Merge pull request #1116 from vector-im/feature/worker_manager
Add some documentation on Workers
2020-04-16 16:28:20 +02:00
onurays
ab3cc90ed5 Share menu item added to video media viewer. 2020-04-16 16:46:00 +03:00
Valere
614127e46b Merge pull request #1240 from vector-im/feature/update_security_notice
Feature/update security notice
2020-04-16 15:33:54 +02:00
Valere
66fc38ad4b Remove GlobalScope usage 2020-04-16 15:09:16 +02:00
Valere
f68e84d9da Update toaster to match web 2020-04-16 15:09:16 +02:00
onurays
2b2e6dd6f8 Share menu item added to image media viewer. 2020-04-16 16:04:37 +03:00
Valere
621e78a864 Merge pull request #1235 from vector-im/feature/upgrate_cross_signing
Add migration state to bootstrap
2020-04-16 15:04:06 +02:00
Benoit Marty
828e972c74 Split long lines 2020-04-16 14:35:55 +02:00
Benoit Marty
15bd7d1c5b Change the regex to detect long lines to allow schema with UTF-8 chars 2020-04-16 14:35:23 +02:00
Benoit Marty
79e81dbdde ktlint 2020-04-16 14:01:15 +02:00
Benoit Marty
f93f50b582 Code readability 2020-04-16 14:00:24 +02:00
Benoit Marty
d934f92ebd Fix bad apostrophe 2020-04-16 13:58:54 +02:00
Waylon Cude
d20cf484ff Merge branch 'develop' of https://github.com/vector-im/riotX-android into develop
Signed-off-by: Waylon Cude <waylon.cude@finzdani.net>
2020-04-16 03:10:06 -07:00
Waylon Cude
ec4458e84a Updated CHANGES.md
Signed-off-by: Waylon Cude <waylon.cude@finzdani.net>
2020-04-16 02:50:58 -07:00
waylon531
6c1719e365 Use a bigger thread pool
This patch makes RiotX use an unbounded thread pool to handle
connections. The default thread pool for the android WorkManager has a
fairly anemic number of threads and I suspect this was causing
performance issues especially because of all the long-running jobs that
happen whenever you sync.

I tested this out on my phone and all of the sluggishness appears to
have gone away. I tested both the debug and release builds to make sure
it wasn't just some release optimization. RiotX is so much snappier now!

This fixes #1221

Signed-off-by: Waylon Cude <waylon.cude@finzdani.net>
2020-04-16 02:44:31 -07:00
Benoit Marty
467f48f1a6 Merge remote-tracking branch 'origin/develop' into develop 2020-04-16 11:33:22 +02:00
Valere
a44cb876c9 move strings to resources 2020-04-16 11:30:10 +02:00
Valere
e79c824913 Fix / password visibility initial state bug 2020-04-16 11:30:10 +02:00
Valere
b8e9cc70f2 fix / line too long 2020-04-16 11:30:10 +02:00
Valere
a2f32307f0 Support back from migrate recovery key 2020-04-16 11:30:10 +02:00
Valere
c1d39cefd5 Fix / avoid upgrade secu popup on account creation 2020-04-16 11:30:10 +02:00
Valere
0edc562120 Fix / test compilation 2020-04-16 11:30:10 +02:00
Valere
8ae2f06044 Add change log 2020-04-16 11:30:10 +02:00
Valere
aa496e6efb Add migration state to bootstrap 2020-04-16 11:30:10 +02:00
Benoit Marty
1372192031 Merge pull request #1091 from vector-im/feature/secure
Add a setting to prevent screenshot of the application, disabled by default (#1027)
2020-04-16 11:27:59 +02:00
Benoit Marty
ea03f76847 Merge pull request #1129 from vector-im/feature/update_password
Change password function implemented.
2020-04-16 11:26:33 +02:00
Benoit Marty
d74a5f9979 Typo 2020-04-16 11:23:41 +02:00
Benoit Marty
febadcc4f6 Merge pull request #1238 from vector-im/feature/thumbnail_info
`mimetype` field is optional
2020-04-16 10:57:29 +02:00
Benoit Marty
17ece54cb0 mimetype field is optional 2020-04-16 10:28:23 +02:00
Benoit Marty
da04a74350 Merge pull request #1143 from MatMaul/refresh-summary
Refresh the whole notifs when cleaning one so the summary get updated
2020-04-15 17:29:36 +02:00
Benoit Marty
634c8947bd Merge branch 'develop' into refresh-summary 2020-04-15 17:29:08 +02:00
Benoit Marty
f6f6fa99fb Merge pull request #1223 from vector-im/feature/cross_signing_as_task
Make initialize cross signing as a task
2020-04-15 17:21:25 +02:00
Benoit Marty
dcfbfc4981 Merge pull request #1230 from vector-im/feature/fix_download_file
Catch network errors during file downloading.
2020-04-15 16:54:31 +02:00
Benoit Marty
6201a9b8ef Merge branch 'develop' into feature/fix_download_file 2020-04-15 16:52:40 +02:00
Benoit Marty
1981d2e9ac Merge pull request #1233 from vector-im/feature/fix_local_echo_no_update
Fix / sending event not always updating
2020-04-15 16:51:16 +02:00
Benoit Marty
66f6b1ecac Merge pull request #1224 from vector-im/feature/composer_shield
Add shield in composer
2020-04-15 16:49:50 +02:00
Benoit Marty
affe2b59da Fix crash 2020-04-15 16:40:01 +02:00
Benoit Marty
757f8ec96a Change password: prevent cancellation when processing 2020-04-15 16:39:05 +02:00
Benoit Marty
bf5e2b96df Change password: bigger margins 2020-04-15 16:39:05 +02:00
Benoit Marty
5666965321 Change password: do not trim passwords 2020-04-15 16:39:05 +02:00
Benoit Marty
7bf1f916c4 Change password: lambda 2020-04-15 16:39:05 +02:00
Benoit Marty
d44e43d94b Change password: update wording of button (Nad's request) 2020-04-15 16:39:05 +02:00
Benoit Marty
9fe32fe915 Change password: hide the preference (as Riot-Web does) if it is not possible to change the password. 2020-04-15 16:39:05 +02:00
Benoit Marty
e21cb3082b Change password: update preference summary. Some people think we know the password. Using Riot-web wording 2020-04-15 16:39:05 +02:00
Benoit Marty
c50bc10f92 Code review: improve layout 2020-04-15 16:38:44 +02:00
Benoit Marty
1b416028b4 Code review: inline the error instead of using toast 2020-04-15 16:38:44 +02:00
Benoit Marty
85493b7532 Code review: use isInvalidPassword() extension everywhere. Also be robust if the Fragment is destroyed 2020-04-15 16:38:44 +02:00
onurays
dbabe0232f Do not override the default timeout. 2020-04-15 16:38:44 +02:00
onurays
dfc8e8ec4c AccountService is created. 2020-04-15 16:38:44 +02:00
onurays
f00db49bda Change password function implemented.
Fixes #528
2020-04-15 16:38:44 +02:00
onurays
b4a3eb2cb3 Fix return the exception instead of empty one. 2020-04-15 16:39:41 +03:00
Valere
81012746c4 Code review / added new key for message hint 2020-04-15 15:39:23 +02:00
Valere
1deacfbb34 Code review 2020-04-15 15:37:43 +02:00
Valere
c35d854776 Add shield in composer 2020-04-15 15:37:43 +02:00
Benoit Marty
c0fa259b40 Add a setting to prevent screenshot of the application, disabled by default (#1027) 2020-04-15 15:04:19 +02:00
Benoit Marty
391d3cb6b5 Merge pull request #1186 from duncanturk/improvement-957-catchup-indicator-on-invite
Fix #957 catchup indicator on invite
2020-04-15 15:02:16 +02:00
Benoit Marty
6d56220d98 Merge branch 'develop' into improvement-957-catchup-indicator-on-invite 2020-04-15 15:00:34 +02:00
Nolan Darilek
2e4d30ef29 Set tickerText to improve accessibility of notifications.
Signed-off-by: Nolan Darilek <nolan@thewordnerd.info>
2020-04-15 07:43:41 -05:00
Benoit Marty
c57fa3f0d0 Merge pull request #1176 from vector-im/feature/target-sdk-29
Increase targetSdk to 29
2020-04-15 14:32:16 +02:00
Benoit Marty
f2bca51046 Merge pull request #1232 from vector-im/feature/translations
string sync
2020-04-15 14:24:39 +02:00
Benoit Marty
6751d88ade Remove .idea exclusion 2020-04-15 13:00:03 +02:00
Benoit Marty
e26a0bc9ae Add Android SDK version 2020-04-15 12:57:15 +02:00
Benoit Marty
6dc517584f Fix merge issue 2020-04-15 12:56:33 +02:00
Benoit Marty
24d0cdef1f Add missing changes of SDK version and build tools 2020-04-15 12:53:28 +02:00
Benoit Marty
6639f89a68 Simpler code 2020-04-15 12:53:08 +02:00
Benoit Marty
0c0e9521f5 Fix lint issue 2020-04-15 09:50:25 +02:00
Valere
f2b684aa9e Fix / user and self signing failing
bad copy paste
2020-04-14 18:23:20 +02:00
Valere
68ca0e9d4b Fix / sending event not always updating 2020-04-14 17:35:11 +02:00
Benoit Marty
e54077e020 String copied to Riot project 2020-04-14 17:26:23 +02:00
Benoit Marty
9111800e7a Create specific string for RiotX 2020-04-14 17:23:59 +02:00
Benoit Marty
ef6847671a Import strings from Riot 2020-04-14 17:13:43 +02:00
Benoit Marty
b8cb0588fe Add new resources 2020-04-14 17:06:05 +02:00
onurays
667b371653 Continue supporting old APIs. 2020-04-14 16:29:11 +03:00
onurays
190fbb95ec Merge branch 'develop' into feature/target-sdk-29
# Conflicts:
#	vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
2020-04-14 13:59:54 +03:00
onurays
f97e08b4e5 Remove .gitignore which belongs to another PR. 2020-04-14 13:12:17 +03:00
onurays
7242cbda40 Catch network errors during file downloading.
Fixes #1229
2020-04-13 17:48:19 +03:00
Valere
9e3011d4c8 Make initialize cross signing as a task 2020-04-10 17:04:34 +02:00
Valere
eb50256af7 Merge pull request #1222 from vector-im/feature/verification_unified_emoji
Uniform emoji representation for all platform
2020-04-10 16:05:42 +02:00
Valere
ccacd20428 Merge branch 'develop' into feature/verification_unified_emoji 2020-04-10 16:05:33 +02:00
Valere
ac46fe9e16 Merge pull request #1218 from vector-im/feature/scan_confirm_update
Feature/scan confirm update
2020-04-10 16:05:02 +02:00
Valere
9cfb83f0d2 Remove outdated translation 2020-04-10 14:31:03 +02:00
Valere
943ba3bebd Fix / string bad argument number - lint 2020-04-10 13:14:12 +02:00
Valere
fccfd00949 Fix / design update 2020-04-10 12:12:21 +02:00
Valere
68323057aa Update change log 2020-04-10 12:12:21 +02:00
Valere
5081361c2d Update Scan confirm flow 2020-04-10 12:12:06 +02:00
Valere
3ba619d45c Merge pull request #1215 from vector-im/feature/ssss_and_backup
Fixes #1214
2020-04-10 12:10:42 +02:00
Valere
f5dc0b38ff Code review 2020-04-10 11:47:54 +02:00
Valere
8357abd455 Added prefix to resources to avoid accidental override 2020-04-10 11:18:41 +02:00
Valere
ede899d78e Fix / Bad smiley emojii 2020-04-10 11:09:16 +02:00
Valere
a703574004 Uniform emoji representation for all platform 2020-04-10 11:02:33 +02:00
Christopher Rossbach
ef2abbfbd4 Merge branch 'develop' into improvement-957-catchup-indicator-on-invite 2020-04-09 11:11:44 +02:00
Valere
7c0137e2dc Fix / await callback suspend forever 2020-04-09 10:46:52 +02:00
Valere
34dec64d9c Fixes #1214 2020-04-08 17:56:12 +02:00
Valere
3968bb3488 Merge pull request #1213 from vector-im/feature/timeline_sum_item
Feature/timeline sum item
2020-04-08 17:08:53 +02:00
Valere
6f2d7aebba code review 2020-04-08 16:37:52 +02:00
Valere
366a35913b Fix alignement 2020-04-08 16:31:01 +02:00
Valere
aec49fe542 Change log 2020-04-08 16:31:01 +02:00
Valere
f04d8b0e03 cleaning 2020-04-08 16:31:01 +02:00
Valere
08af61b778 rename layout 2020-04-08 16:31:01 +02:00
Valere
277f35a352 Merge room creation events in one summary 2020-04-08 16:31:01 +02:00
Valere
68512e475f WIP 2020-04-08 16:31:01 +02:00
Valere
0eff00ebee Merge pull request #1211 from vector-im/feature/fix_gossiping_to_early
Fix / Send gossip request on other done received
2020-04-08 16:30:17 +02:00
Onuray Sahin
8a4a288074 Merge branch 'develop' into feature/target-sdk-29 2020-04-08 14:15:02 +03:00
onurays
5b1f887760 Code review fixes. 2020-04-08 14:10:31 +03:00
onurays
dcb6af6c45 .gitignore file added. 2020-04-08 12:40:29 +03:00
Christopher Rossbach
3ff5952417 Merge branch 'develop' into improvement-957-catchup-indicator-on-invite 2020-04-07 20:53:31 +02:00
Valere
b480eb3688 Update change log 2020-04-07 19:20:49 +02:00
Valere
12abca1b80 Fix / Send gossip request on other done received 2020-04-07 19:09:56 +02:00
Valere
1d8ed387bc Merge pull request #1206 from vector-im/feature/gossip_keybackup_key
Feature/gossip keybackup key
2020-04-07 18:06:05 +02:00
Christopher Rossbach
5521c094f7 fix typing difference 2020-04-07 17:50:01 +02:00
Christopher Rossbach
222b72a014 Merge branch 'develop' into improvement-957-catchup-indicator-on-invite 2020-04-07 17:46:23 +02:00
Valere
8904ca27f2 clean 2020-04-07 15:30:19 +02:00
Valere
6c5da97c16 code review 2020-04-07 15:25:31 +02:00
Valere
d4d73db5ae keybackup gossip test 2020-04-07 15:15:45 +02:00
Valere
1a436f962f update change log
cleaning
2020-04-07 15:15:45 +02:00
Valere
dc61ee61f5 Fix / wrong export format 2020-04-07 15:15:45 +02:00
Valere
5b4b5e7a57 Gossip keybackup key after verification! 2020-04-07 15:15:45 +02:00
Valere
0164f94047 Merge pull request #1198 from vector-im/feature/crosssigning_bootstrap
Feature/crosssigning bootstrap
2020-04-07 15:14:54 +02:00
Valere
153587bd82 Merge branch 'develop' into feature/crosssigning_bootstrap 2020-04-07 15:14:43 +02:00
Valere
326f2e99fb klint 2020-04-06 10:00:59 +02:00
Valere
1dfd6f232a Code quality / line too long 2020-04-03 18:59:21 +02:00
Valere
42d61944b5 Merge pull request #1204 from vector-im/feature/increase_file_log_size
Increase file logger size
2020-04-03 16:40:18 +02:00
Valere
50a8ffeca1 Merge pull request #1203 from vector-im/feature/fix_e2e_null_alg
Feature/fix e2e null alg
2020-04-03 15:57:56 +02:00
Valere
f605bb8270 Code review 2020-04-03 15:57:05 +02:00
Valere
7ffb6113a4 Increase file logger size 2020-04-03 13:53:34 +02:00
Valere
156e6114c1 Updade change log 2020-04-03 13:43:40 +02:00
Valere
c91bc82cd9 Fix / Ensure encryption set even if faield to fetch members 2020-04-03 13:35:09 +02:00
Valere
8b481e2294 Remove dead code 2020-04-03 13:34:38 +02:00
Valere
92bf3f1349 Update change log
+ code quality
2020-04-03 11:14:14 +02:00
Valere
6474735662 Fix / devtools was not showing all json numbers 2020-04-03 10:31:44 +02:00
Valere
45c5626267 Add generate key option 2020-04-02 18:30:43 +02:00
Valere
c27264761d Back /Skip navigation 2020-04-02 18:11:52 +02:00
Valere
c6abfa14ea Fix / Bind continue button 2020-04-02 16:54:30 +02:00
Valere
2f237cf17b klint 2020-04-02 16:51:40 +02:00
Valere
bf5ba99653 Full bootstrap flow initial commit 2020-04-02 16:51:03 +02:00
Valere
8ecdac7c31 Fixes #1191 2020-03-31 17:08:11 +02:00
Valere
a40dd31543 Bootstrap bottomsheet 2020-03-27 17:09:18 +01:00
Christopher Rossbach
dff89cb2e1 document changes
Signed-off-by: Christopher Rossbach <31703168+duncanturk@users.noreply.github.com>
2020-03-27 14:41:48 +01:00
Christopher Rossbach
cdbb657961 Fix #957 by incrementing the catchup count on home icon.
Signed-off-by: Christopher Rossbach <31703168+duncanturk@users.noreply.github.com>
2020-03-27 14:24:42 +01:00
Onuray Sahin
443d45db6a Merge branch 'develop' into feature/target-sdk-29 2020-03-27 12:21:39 +03:00
Valere
a995615f87 Fix / protect against crash during migration 2020-03-26 18:20:12 +01:00
Valere
024c62515c Merge pull request #1181 from vector-im/feature/update_shield_logic
Update shield logic for DM
2020-03-26 16:58:10 +01:00
Valere
75e66a6550 Merge branch 'develop' into feature/update_shield_logic 2020-03-26 16:58:01 +01:00
Onuray Sahin
e2f7890bb8 Merge branch 'develop' into feature/target-sdk-29 2020-03-26 17:39:22 +03:00
onurays
0a77d5014e Fix nullability issues. 2020-03-26 17:19:29 +03:00
Valere
91464a071e Merge pull request #1180 from vector-im/feature/complete_security_design_update
Feature/complete security design update
2020-03-26 14:31:09 +01:00
Valere
5244612ef6 Update shield logic for DM 2020-03-26 14:26:34 +01:00
Valere
e51439ade0 updage change log 2020-03-26 14:10:19 +01:00
Valere
acd90657c7 Fix / prevent cancel when in conclusion fragment 2020-03-26 14:09:41 +01:00
onurays
e5482d48c0 Remove deprecated & unused ImageTools class. 2020-03-26 16:02:50 +03:00
onurays
5816a04a37 Use MediaStore instead of deprecated addCompletedDownload since Android Q. 2020-03-26 15:58:31 +03:00
onurays
4b7da9ae6b Replace deprecated getExternalStoragePublicDirectory with getExternalFilesDir. 2020-03-26 15:57:21 +03:00
onurays
f7cbc01023 Replace deprecated PreferenceManager with androidx version. 2020-03-26 15:56:33 +03:00
Valere
1de57bbf3b Fix / cancel copy when verifying other 2020-03-26 13:49:48 +01:00
Valere
42a8c561db Fix / Verification was not cancelled on back in ready state 2020-03-26 13:49:48 +01:00
Valere
5bef9aef6a Fix / add specific copy for other verif cancel confirm 2020-03-26 13:49:48 +01:00
Valere
f8c1ec985f re-prompt to verify on restart 2020-03-26 13:49:48 +01:00
onurays
12429d8091 Merge develop into the branch. 2020-03-26 14:39:50 +03:00
Onuray Sahin
3bb5e127d6 Merge pull request #1155 from vector-im/feature/multipicker
Multiple attachment picker implementation
2020-03-26 14:30:02 +03:00
Valere
1d46b523b9 Merge pull request #1172 from vector-im/feature/ensure_olm_account_unicity
Feature/ensure olm account unicity
2020-03-26 12:11:09 +01:00
Valere
6721f337bd Merge branch 'develop' into feature/ensure_olm_account_unicity 2020-03-26 12:11:00 +01:00
Valere
535cdf0ef5 Merge pull request #1162 from vector-im/feature/xs_detect_new_session
Feature/xs detect new session
2020-03-26 12:06:18 +01:00
Valere
19990b27bb Code review 2020-03-26 10:52:02 +01:00
Valere
4b3c5d5135 update change log 2020-03-26 10:35:43 +01:00
Valere
b6fe80faf4 Fix / device key could be rotated
MXOlmDevice constructor access IMXStore before is open (dagger)
2020-03-26 10:35:43 +01:00
Valere
638970fa77 Merge pull request #1175 from vector-im/feature/fix_ssss_symetric_get_secret
Fixes #1174
2020-03-26 10:34:49 +01:00
onurays
c63f3edb06 Initial fixes to support targetSdk 29. 2020-03-26 12:06:50 +03:00
Valere
9a6fe1af4e Fixes #1174 2020-03-26 09:49:34 +01:00
onurays
a01482dca4 Use Timber log instead of printStackTrace. 2020-03-25 18:51:55 +03:00
onurays
5db1010e47 Catch exceptions if the file cannot be decoded. 2020-03-25 18:39:35 +03:00
onurays
6130a0a654 Remove unused toString(). 2020-03-25 18:34:04 +03:00
onurays
3c1e1090e7 Avoid UNCHECKED_CAST. 2020-03-25 18:20:34 +03:00
onurays
5cb47dae35 Return a failure message if the file cannot be opened. 2020-03-25 18:03:20 +03:00
onurays
f68e98b2c7 Do not resize video thumbnail. 2020-03-25 17:35:18 +03:00
Valere
420a55da76 klint 2020-03-25 13:53:06 +01:00
onurays
f9aed28732 grantUriPermission to handle incoming sharing. 2020-03-25 15:51:15 +03:00
Valere
e30c17eab7 Update Changes log 2020-03-24 18:09:54 +01:00
Valere
2c9a8865bf Cancel all on going to settings 2020-03-24 17:48:16 +01:00
Valere
bddd70afdb Fix / IllegalState after token invalidation 2020-03-24 17:48:01 +01:00
Valere
c4388348f7 Show cancelled screen instead of dismissing 2020-03-24 17:38:46 +01:00
Valere
ee7828a445 Code quality 2020-03-24 16:21:22 +01:00
Valere
37ac45c90a Fix / handling of back 2020-03-24 15:51:09 +01:00
Valere
63d3bf93f2 Fix / for now keep old toaster 2020-03-24 15:50:49 +01:00
Valere
2de8865730 Fix / Key Request recipients 2020-03-24 15:49:41 +01:00
Valere
fcffe1f3c3 Clear alert when device is verified 2020-03-24 15:21:03 +01:00
Valere
cfcec04029 code cleaning 2020-03-24 14:57:51 +01:00
Valere
b56a41bec7 Custom alert design 2020-03-24 14:56:57 +01:00
onurays
6bf89aeac9 Remove JPEG_ prefix from file name. 2020-03-24 12:37:37 +03:00
onurays
e583c03751 Add documentation. 2020-03-24 12:32:37 +03:00
onurays
d20b1cb64a Add documentation. 2020-03-24 12:15:14 +03:00
Valere
22642e71a3 cleaning 2020-03-24 10:06:36 +01:00
Valere
6e85b20b0e Update copy for riotX default device Name 2020-03-24 10:06:30 +01:00
Valere
fcd290410e Also cancel pending request on back 2020-03-24 10:06:15 +01:00
onurays
727d86236b ImageUtils created with helper functions. 2020-03-24 11:31:27 +03:00
onurays
2651f82337 Refactor duplicated code. 2020-03-24 11:03:41 +03:00
Valere
3b62402cfe Fix / ensure keys trust is updated before checking devices 2020-03-23 19:15:29 +01:00
Valere
6cc8d1b205 Fix / concurrent start broke QR verification 2020-03-23 18:43:52 +01:00
Valere
49e5fafb2d New sign in detection flow 2020-03-23 16:27:32 +01:00
Valere
e36367c040 Fix / sending secret encryption + refactoring 2020-03-23 16:27:17 +01:00
onurays
f7fd23b153 App integration to the new multipicker library. 2020-03-23 16:31:32 +03:00
Valere
4f70c40b1a Refactor + share secret window implementation 2020-03-23 11:13:33 +01:00
onurays
5b875e0571 CameraPicker & incoming share implementation. 2020-03-22 18:27:59 +03:00
onurays
6db0de321c Initial implementation of multipicker. 2020-03-20 12:12:59 +03:00
Valere
0c1f30208d Fix / Epoxy divider not showing 2020-03-19 11:23:15 +01:00
Valere
08cfe79625 Merge pull request #1153 from vector-im/feature/fix_841
Fixes #841
2020-03-18 14:50:56 +01:00
Valere
22a599633d Merge pull request #1152 from vector-im/feature/concurrent_start_verification
Verif / handle concurrent start
2020-03-18 14:50:33 +01:00
Valere
14acbb2b4d Update changes 2020-03-18 12:06:37 +01:00
Valere
6f5bebedf8 Fixes #841 2020-03-18 11:53:38 +01:00
Valere
6fe77eba72 code review 2020-03-18 11:25:49 +01:00
Valere
286a5081ff Verif / handle concurrent start
Fixes #794
2020-03-18 10:07:57 +01:00
Valere
56c241c9bd Merge pull request #1140 from vector-im/feature/gossiping_work
Feature/gossiping work
2020-03-17 15:48:11 +01:00
Valere
68151d838f Update changes 2020-03-17 15:10:27 +01:00
Valere
572b174cfe code quality 2020-03-17 14:42:55 +01:00
Valere
b71d8185a2 Fix / gossiping sent to soon results in not getting keys
Overall improovment of logs
2020-03-17 14:40:05 +01:00
Valere
8051d9e3be cleaning 2020-03-17 14:40:05 +01:00
Valere
1bf8fef292 Fix realm migration 2020-03-17 14:40:05 +01:00
Valere
b8a9397e73 hide reRequest behind developer mode 2020-03-17 14:40:05 +01:00
Valere
009d691d5b post merge fix 2020-03-17 14:40:05 +01:00
Valere
6933159245 Remove refresh menu 2020-03-17 14:40:05 +01:00
Valere
75549c41e0 View source in audit + clean 2020-03-17 14:40:05 +01:00
Valere
5e2f888eaf Request secret from mobile to web 2020-03-17 14:40:05 +01:00
Valere
d3d6d44665 Post rebase fix 2020-03-17 14:40:05 +01:00
Valere
fc6225a7ac Gossiping refactoring 2020-03-17 14:40:05 +01:00
Valere
3639007985 Cancel transaction if failed to decrypt other part events 2020-03-17 14:40:05 +01:00
Valere
d5137897c1 Fix / crash No JsonAdapter for GossipingToDeviceObject 2020-03-17 14:40:05 +01:00
Valere
b67735c31a Incoming Secret Share request support
crypto DB migration
2020-03-17 14:40:05 +01:00
Valere
8ff31ac49d cleaning klint 2020-03-17 14:40:05 +01:00
Valere
5e0235e48d Add option to recover from backup + hide if not applicable 2020-03-17 14:40:05 +01:00
Valere
9e63a3219c Add Re-Request keys for fail to decrypt 2020-03-17 14:40:05 +01:00
Valere
757e90986e Key Req Dev tool initial commit 2020-03-17 14:40:05 +01:00
Valere
06fc5c2dd9 Log tunning 2020-03-17 14:40:05 +01:00
Valere
20dbe2dd0d version ++ 2020-03-17 14:18:08 +01:00
Valere
6f56c74e9d Merge branch 'release/0.18.1' into develop 2020-03-17 11:46:55 +01:00
unclejay
a2367ef14f added network proxy configuration 2020-03-16 21:12:15 +01:00
Mathieu Velten
687884431e Refresh the whole notifs when cleaning one so the summary get updated
Fix summary notification staying after "mark as read"
2020-03-13 23:42:28 +01:00
Benoit Marty
a8e19f3cc9 Understanding things 2020-03-06 15:57:49 +01:00
Benoit Marty
cb4752812a Hide private key 2020-03-06 15:53:55 +01:00
Benoit Marty
ccd9d2961d Cleanup 2020-03-06 15:45:39 +01:00
Benoit Marty
d1db17f244 Add doc on workers 2020-03-06 15:45:26 +01:00
Benoit Marty
aa4327c4da Add doc and log error from the workers 2020-03-06 15:44:55 +01:00
960 changed files with 44277 additions and 7160 deletions

View File

@@ -12,12 +12,15 @@
<w>fdroid</w> <w>fdroid</w>
<w>gplay</w> <w>gplay</w>
<w>hmac</w> <w>hmac</w>
<w>homeserver</w>
<w>ktlint</w> <w>ktlint</w>
<w>linkified</w> <w>linkified</w>
<w>linkify</w> <w>linkify</w>
<w>megolm</w> <w>megolm</w>
<w>msisdn</w> <w>msisdn</w>
<w>msisdns</w>
<w>pbkdf</w> <w>pbkdf</w>
<w>pids</w>
<w>pkcs</w> <w>pkcs</w>
<w>riotx</w> <w>riotx</w>
<w>signin</w> <w>signin</w>
@@ -25,6 +28,7 @@
<w>signup</w> <w>signup</w>
<w>ssss</w> <w>ssss</w>
<w>threepid</w> <w>threepid</w>
<w>unwedging</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

View File

@@ -23,10 +23,10 @@ android:
- platform-tools - platform-tools
# The BuildTools version used by your project # The BuildTools version used by your project
- build-tools-28.0.3 - build-tools-29.0.3
# The SDK version used to compile your project # The SDK version used to compile your project
- android-28 - android-29
before_cache: before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock

View File

@@ -1,3 +1,115 @@
Changes in RiotX 0.21.0 (2020-05-28)
===================================================
Features ✨:
- Identity server support (#607)
- Switch language support (#41)
- Display list of attachments of a room (#860)
Improvements 🙌:
- Better connectivity lost indicator when airplane mode is on
- Add a setting to hide redacted events (#951)
- Render formatted_body for m.notice and m.emote (#1196)
- Change icon to magnifying-glass to filter room (#1384)
Bugfix 🐛:
- After jump to unread, newer messages are never loaded (#1008)
- Fix issues with FontScale switch (#69, #645)
- "Seen by" uses 12h time (#1378)
- Enable markdown (if active) when sending emote (#734)
- Screenshots for Rageshake now includes Dialogs such as BottomSheet (#1349)
SDK API changes ⚠️:
- initialize with proxy configuration
Other changes:
- support new key agreement method for SAS (#1374)
Changes in RiotX 0.20.0 (2020-05-15)
===================================================
Features ✨:
- Add Direct Shortcuts (#652)
Improvements 🙌:
- Invite member(s) to an existing room (#1276)
- Improve notification accessibility with ticker text (#1226)
- Support homeserver discovery from MXID (DISABLED: waiting for design) (#476)
Bugfix 🐛:
- Fix | Verify Manually by Text crashes if private SSK not known (#1337)
- Sometimes the same device appears twice in the list of devices of a user (#1329)
- Random Crashes while doing sth with cross signing keys (#1364)
- Crash | crash while restoring key backup (#1366)
SDK API changes ⚠️:
- excludedUserIds parameter added to the UserService.getPagedUsersLive() function
Changes in RiotX 0.19.0 (2020-05-04)
===================================================
Features ✨:
- Change password (#528)
- Cross-Signing | Support SSSS secret sharing (#944)
- Cross-Signing | Verify new session from existing session (#1134)
- Cross-Signing | Bootstraping cross signing with 4S from mobile (#985)
- Save media files to Gallery (#973)
- Account deactivation (with password only) (#35)
Improvements 🙌:
- Verification DM / Handle concurrent .start after .ready (#794)
- Reimplementation of multiple attachment picker
- Cross-Signing | Update Shield Logic for DM (#963)
- Cross-Signing | Complete security new session design update (#1135)
- Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201)
- Cross-Signing | Gossip key backup recovery key (#1200)
- Show room encryption status as a bubble tile (#1078)
- UX/UI | Add indicator to home tab on invite (#957)
- Cross-Signing | Restore history after recover from passphrase (#1214)
- Cross-Sign | QR code scan confirmation screens design update (#1187)
- Emoji Verification | It's not the same butterfly! (#1220)
- Cross-Signing | Composer decoration: shields (#1077)
- Cross-Signing | Migrate existing keybackup to cross signing with 4S from mobile (#1197)
- Show a warning dialog if the text of the clicked link does not match the link target (#922)
- Cross-Signing | Consider not using a spinner on the 'complete security' prompt (#1271)
- Restart broken Olm sessions ([MSC1719](https://github.com/matrix-org/matrix-doc/pull/1719))
- Cross-Signing | Hide Use recovery key when 4S is not setup (#1007)
- Cross-Signing | Trust account xSigning keys by entering Recovery Key (select file or copy) #1199
- E2E timeline decoration (#1279)
- Manage Session Settings / Cross Signing update (#1295)
- Cross-Signing | Review sessions toast update old vs new (#1293, #1306)
Bugfix 🐛:
- Fix summary notification staying after "mark as read"
- Missing avatar/displayname after verification request message (#841)
- Crypto | RiotX sometimes rotate the current device keys (#1170)
- RiotX can't restore cross signing keys saved by web in SSSS (#1174)
- Cross- Signing | After signin in new session, verification paper trail in DM is off (#1191)
- Failed to encrypt message in room (message stays in red), [thanks to pwr22] (#925)
- Cross-Signing | web <-> riotX After QR code scan, gossiping fails (#1210)
- Fix crash when trying to download file without internet connection (#1229)
- Local echo are not updated in timeline (for failed & encrypted states)
- Render image event even if thumbnail_info does not have mimetype defined (#1209)
- RiotX now uses as many threads as it needs to do work and send messages (#1221)
- Fix issue with media path (#1227)
- Add user to direct chat by user id (#1065)
- Use correct URL for SSO connection (#1178)
- Emoji completion :tada: does not completes to 🎉 like on web (#1285)
- Fix bad Shield Logic for DM (#963)
Translations 🗣:
- Weblate now create PR directly to RiotX GitHub project
SDK API changes ⚠️:
- Increase targetSdkVersion to 29
Build 🧱:
- Compile with Android SDK 29 (Android Q)
Other changes:
- Add a setting to prevent screenshots of the application, disabled by default (#1027)
- Increase File Logger capacities ( + use dev log preferences)
Changes in RiotX 0.18.1 (2020-03-17) Changes in RiotX 0.18.1 (2020-03-17)
=================================================== ===================================================
@@ -404,6 +516,7 @@ Bugfix:
- Fix messages with empty `in_reply_to` not rendering (#447) - Fix messages with empty `in_reply_to` not rendering (#447)
- Fix clear cache (#408) and Logout (#205) - Fix clear cache (#408) and Logout (#205)
- Fix `(edited)` link can be copied to clipboard (#402) - Fix `(edited)` link can be copied to clipboard (#402)
- KeyBackup / SSSS | Should get the key from SSSS instead of asking recovery Key (#1163)
Build: Build:
- Split APK: generate one APK per arch, to reduce APK size of about 30% - Split APK: generate one APK per arch, to reduce APK size of about 30%

View File

@@ -13,6 +13,24 @@ Dedicated room for RiotX: [![RiotX Android Matrix room #riot-android:matrix.org]
Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`). Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`).
Please ensure that your using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them. Please ensure that your using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them.
### Template
An Android Studio template has been added to the project to help creating all files needed when adding a new screen to the application. Fragment, ViewModel, Activity, etc.
To install the template (to be done only once):
- Go to folder `./tools/template`.
- Run the script `./configure.sh`.
- Restart Android Studio.
To create a new screen:
- First create a new package in your code.
- Then right click on the package, and select `New/New Vector/RiotX Feature`.
- Follow the Wizard, especially replace `Main` by something more relevant to your feature.
- Click on `Finish`.
- Remainning steps are described as TODO in the generated files, or will be pointed out by the compilator, or at runtime :)
Note that if the templates are modified, the only things to do is to restart Android Studio for the change to take effect.
## Compilation ## Compilation
For now, the Matrix SDK and the RiotX application are in the same project. So there is no specific thing to do, this project should compile without any special action. For now, the Matrix SDK and the RiotX application are in the same project. So there is no specific thing to do, this project should compile without any special action.

92
docs/identity_server.md Normal file
View File

@@ -0,0 +1,92 @@
# Identity server
Issue: #607
PR: #1354
## Introduction
Identity Servers support contact discovery on Matrix by letting people look up Third Party Identifiers to see if the owner has publicly linked them with their Matrix ID.
## Implementation
The current implementation was Inspired by the code from Riot-Android.
Difference though (list not exhaustive):
- Only API v2 is supported (see https://matrix.org/docs/spec/identity_service/latest)
- Homeserver has to be up to date to support binding (Versions.isLoginAndRegistrationSupportedBySdk() has to return true)
- The SDK managed the session and client secret when binding ThreePid. Those data are not exposed to the client.
- The SDK supports incremental sendAttempt (this is not used by RiotX)
- The "Continue" button is now under the information, and not as the same place that the checkbox
- The app can cancel a binding. Current data are erased from DB.
- The API (IdentityService) is improved.
- A new DB to store data related to the identity server management.
Missing features (list not exhaustive):
- Invite by 3Pid (will be in a dedicated PR)
- Add email or phone to account (not P1, can be done on Riot-Web)
- List email and phone of the account (could be done in a dedicated PR)
- Search contact (not P1)
- Logout from identity server when user sign out or deactivate his account.
## Related MSCs
The list can be found here: https://matrix.org/blog/2019/09/27/privacy-improvements-in-synapse-1-4-and-riot-1-4
## Steps and requirements
- Only one identity server by account can be set. The user's choice is stored in account data with key `m.identity_server`. But every clients will managed its own token to log in to the identity server
```json
{
"type": "m.identity_server",
"content": {
"base_url": "https://matrix.org"
}
}
```
- The accepted terms are stored in the account data:
```json
{
"type": "m.accepted_terms",
"content": {
"accepted": [
"https://vector.im/identity-server-privacy-notice-1"
]
}
}
```
- Default identity server URL, from Wellknown data is proposed to the user.
- Identity server can be set
- Identity server can be changed on another user's device, so when the change is detected (thanks to account data sync) RiotX should properly disconnect from a previous identity server (I think it was not the case in Riot-Android, where we keep the token forever)
- Registration to the identity server is managed with an openId token
- Terms of service can be accepted when configuring the identity server.
- Terms of service can be accepted after, if they change.
- Identity server can be modified
- Identity server can be disconnected with a warning dialog, with special content if there are current bound 3pid on this identity server.
- Email can be bound
- Email can be unbound
- Phone can be bound
- Phone can be unbound
- Look up can be performed, to get matrixIds from local contact book (phone and email): Android permission correctly handled (not done yet)
- Look up pepper can be updated if it is rotated on the identity server
- Invitation using 3PID can be done (See #548) (not done yet)
- Homeserver access-token will never be sent to an identity server
- When user sign-out: logout from the identity server if any.
- When user deactivate account: logout from the identity server if any.
## Screens
### Settings
Identity server settings can be accessed from the internal setting of the application, both from "Discovery" section and from identity detail section.
### Discovery screen
This screen displays the identity server configuration and the binding of the user's ThreePid (email and msisdn). This is the main screen of the feature.
### Set identity server screen
This screen is a form to set a new identity server URL
## Ref:
- https://matrix.org/blog/2019/09/27/privacy-improvements-in-synapse-1-4-and-riot-1-4 is a good summary of the role of an Identity server and the proper way to configure and use it in respect to the privacy and the consent of the user.
- API documentation: https://matrix.org/docs/spec/identity_service/latest
- vector.im TOS: https://vector.im/identity-server-privacy-notice

View File

@@ -38,10 +38,10 @@ When the client receives the new information, it immediately sends another reque
This effectively emulates a server push feature. This effectively emulates a server push feature.
The HTTP long Polling can be fine tuned in the **SDK** using two parameters: The HTTP long Polling can be fine tuned in the **SDK** using two parameters:
* timout (Sync request timeout) * timeout (Sync request timeout)
* delay (Delay between each sync) * delay (Delay between each sync)
**timeout** is a server paramter, defined by: **timeout** is a server parameter, defined by:
``` ```
The maximum time to wait, in milliseconds, before returning this request.` The maximum time to wait, in milliseconds, before returning this request.`
If no events (or other data) become available before this time elapses, the server will return a response with empty fields. If no events (or other data) become available before this time elapses, the server will return a response with empty fields.

View File

@@ -57,7 +57,7 @@ We get credential (200)
```json ```json
{ {
"user_id": "@benoit0816:matrix.org", "user_id": "@alice:matrix.org",
"access_token": "MDAxOGxvY2F0aW9uIG1hdHREDACTEDb2l0MDgxNjptYXRyaXgub3JnCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gfnYrSypfdTtkNXIuNWx1KgowMDJmc2lnbmF0dXJlIOsh1XqeAkXexh4qcofl_aR4kHJoSOWYGOhE7-ubX-DZCg", "access_token": "MDAxOGxvY2F0aW9uIG1hdHREDACTEDb2l0MDgxNjptYXRyaXgub3JnCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gfnYrSypfdTtkNXIuNWx1KgowMDJmc2lnbmF0dXJlIOsh1XqeAkXexh4qcofl_aR4kHJoSOWYGOhE7-ubX-DZCg",
"home_server": "matrix.org", "home_server": "matrix.org",
"device_id": "GTVREDALBF", "device_id": "GTVREDALBF",
@@ -128,6 +128,8 @@ We get the credentials (200)
} }
``` ```
It's worth noting that the response from the homeserver contains the userId of Alice.
### Login with Msisdn ### Login with Msisdn
Not supported yet in RiotX Not supported yet in RiotX

View File

@@ -8,7 +8,7 @@
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true android.enableJetifier=true
android.useAndroidX=true android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx8192m
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

View File

@@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
android { android {
compileSdkVersion 28 compileSdkVersion 29
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 29
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"

View File

@@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.android.api.util.toOptional
import io.reactivex.Completable
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@@ -95,6 +96,10 @@ class RxRoom(private val room: Room) {
fun liveNotificationState(): Observable<RoomNotificationState> { fun liveNotificationState(): Observable<RoomNotificationState> {
return room.getLiveRoomNotificationState().asObservable() return room.getLiveRoomNotificationState().asObservable()
} }
fun invite(userId: String, reason: String? = null): Completable = completableBuilder<Unit> {
room.invite(userId, reason, it)
}
} }
fun Room.rx(): RxRoom { fun Room.rx(): RxRoom {

View File

@@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.identity.ThreePid
import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
@@ -31,6 +32,8 @@ import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.android.api.util.toOptional
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@@ -58,6 +61,13 @@ class RxSession(private val session: Session) {
} }
} }
fun liveMyDeviceInfo(): Observable<List<DeviceInfo>> {
return session.cryptoService().getLiveMyDevicesInfo().asObservable()
.startWithCallable {
session.cryptoService().getMyDevicesInfo()
}
}
fun liveSyncState(): Observable<SyncState> { fun liveSyncState(): Observable<SyncState> {
return session.getSyncStateLive().asObservable() return session.getSyncStateLive().asObservable()
} }
@@ -81,8 +91,13 @@ class RxSession(private val session: Session) {
return session.getIgnoredUsersLive().asObservable() return session.getIgnoredUsersLive().asObservable()
} }
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> { fun livePagedUsers(filter: String? = null, excludedUserIds: Set<String>? = null): Observable<PagedList<User>> {
return session.getPagedUsersLive(filter).asObservable() return session.getPagedUsersLive(filter, excludedUserIds).asObservable()
}
fun liveThreePIds(refreshData: Boolean): Observable<List<ThreePid>> {
return session.getThreePidsLive(refreshData).asObservable()
.startWithCallable { session.getThreePids() }
} }
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder { fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
@@ -123,6 +138,13 @@ class RxSession(private val session: Session) {
} }
} }
fun liveCrossSigningPrivateKeys(): Observable<Optional<PrivateKeysInfo>> {
return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asObservable()
.startWithCallable {
session.cryptoService().crossSigningService().getCrossSigningPrivateKeys().toOptional()
}
}
fun liveAccountData(types: Set<String>): Observable<List<UserAccountDataEvent>> { fun liveAccountData(types: Set<String>): Observable<List<UserAccountDataEvent>> {
return session.getLiveAccountDataEvents(types).asObservable() return session.getLiveAccountDataEvents(types).asObservable()
.startWithCallable { .startWithCallable {

View File

@@ -19,12 +19,12 @@ androidExtensions {
} }
android { android {
compileSdkVersion 28 compileSdkVersion 29
testOptions.unitTests.includeAndroidResources = true testOptions.unitTests.includeAndroidResources = true
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 29
versionCode 1 versionCode 1
versionName "0.0.1" versionName "0.0.1"
// Multidex is useful for tests // Multidex is useful for tests
@@ -71,6 +71,15 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "1.8"
} }
sourceSets {
androidTest {
java.srcDirs += "src/sharedTest/java"
}
test {
java.srcDirs += "src/sharedTest/java"
}
}
} }
static def gitRevision() { static def gitRevision() {
@@ -97,6 +106,7 @@ dependencies {
def coroutines_version = "1.3.2" def coroutines_version = "1.3.2"
def markwon_version = '3.1.0' def markwon_version = '3.1.0'
def daggerVersion = '2.25.4' def daggerVersion = '2.25.4'
def work_version = '2.3.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
@@ -118,7 +128,7 @@ dependencies {
implementation "ru.noties.markwon:core:$markwon_version" implementation "ru.noties.markwon:core:$markwon_version"
// Image // Image
implementation 'androidx.exifinterface:exifinterface:1.1.0' implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01'
implementation 'id.zelory:compressor:3.0.0' implementation 'id.zelory:compressor:3.0.0'
// Database // Database
@@ -126,7 +136,7 @@ dependencies {
kapt 'dk.ilios:realmfieldnameshelper:1.1.1' kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
// Work // Work
implementation "androidx.work:work-runtime-ktx:2.3.3" implementation "androidx.work:work-runtime-ktx:$work_version"
// FP // FP
implementation "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-core:$arrow_version"
@@ -148,6 +158,9 @@ dependencies {
// Bus // Bus
implementation 'org.greenrobot:eventbus:3.1.1' implementation 'org.greenrobot:eventbus:3.1.1'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0' debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0' releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0' androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
@@ -159,6 +172,8 @@ dependencies {
testImplementation 'io.mockk:mockk:1.9.2.kotlin12' testImplementation 'io.mockk:mockk:1.9.2.kotlin12'
testImplementation 'org.amshove.kluent:kluent-android:1.44' testImplementation 'org.amshove.kluent:kluent-android:1.44'
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
// Plant Timber tree for test
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
androidTestImplementation 'androidx.test:core:1.2.0' androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:runner:1.2.0'
@@ -170,5 +185,6 @@ dependencies {
androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12' androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12'
androidTestImplementation "androidx.arch.core:core-testing:$arch_version" androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
// Plant Timber tree for test
androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
} }

View File

@@ -18,10 +18,15 @@ package im.vector.matrix.android
import android.content.Context import android.content.Context
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import im.vector.matrix.android.test.shared.createTimberTestRule
import org.junit.Rule
import java.io.File import java.io.File
interface InstrumentedTest { interface InstrumentedTest {
@Rule
fun timberTestRule() = createTimberTestRule()
fun context(): Context { fun context(): Context {
return ApplicationProvider.getApplicationContext() return ApplicationProvider.getApplicationContext()
} }

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 im.vector.matrix.android.account
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.failure.isInvalidPassword
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants
import org.amshove.kluent.shouldBeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class ChangePasswordTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
companion object {
private const val NEW_PASSWORD = "this is a new password"
}
@Test
fun changePasswordTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
// Change password
commonTestHelper.doSync<Unit> {
session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD, it)
}
// Try to login with the previous password, it will fail
val throwable = commonTestHelper.logAccountWithError(session.myUserId, TestConstants.PASSWORD)
throwable.isInvalidPassword().shouldBeTrue()
// Try to login with the new password, should work
val session2 = commonTestHelper.logIntoAccount(session.myUserId, NEW_PASSWORD, SessionTestParams(withInitialSync = false))
commonTestHelper.signOutAndClose(session)
commonTestHelper.signOutAndClose(session2)
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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 im.vector.matrix.android.account
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.auth.data.LoginFlowResult
import im.vector.matrix.android.api.auth.registration.RegistrationResult
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants
import im.vector.matrix.android.common.TestMatrixCallback
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class DeactivateAccountTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@Test
fun deactivateAccountTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
// Deactivate the account
commonTestHelper.doSync<Unit> {
session.deactivateAccount(TestConstants.PASSWORD, false, it)
}
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
val throwable = commonTestHelper.logAccountWithError(session.myUserId, TestConstants.PASSWORD)
// Test the error
assertTrue(throwable is Failure.ServerError
&& throwable.error.code == MatrixError.M_USER_DEACTIVATED
&& throwable.error.message == "This account has been deactivated")
// Try to create an account with the deactivate account user id, it will fail (M_USER_IN_USE)
val hs = commonTestHelper.createHomeServerConfig()
commonTestHelper.doSync<LoginFlowResult> {
commonTestHelper.matrix.authenticationService.getLoginFlow(hs, it)
}
var accountCreationError: Throwable? = null
commonTestHelper.waitWithLatch {
commonTestHelper.matrix.authenticationService
.getRegistrationWizard()
.createAccount(session.myUserId.substringAfter("@").substringBefore(":"),
TestConstants.PASSWORD,
null,
object : TestMatrixCallback<RegistrationResult>(it, false) {
override fun onFailure(failure: Throwable) {
accountCreationError = failure
super.onFailure(failure)
}
})
}
// Test the error
accountCreationError.let {
assertTrue(it is Failure.ServerError
&& it.error.code == MatrixError.M_USER_IN_USE)
}
// No need to close the session, it has been deactivated
}
}

View File

@@ -28,16 +28,17 @@ import im.vector.matrix.android.api.auth.data.LoginFlowResult
import im.vector.matrix.android.api.auth.registration.RegistrationResult import im.vector.matrix.android.api.auth.registration.RegistrationResult
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.sync.SyncState
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@@ -87,7 +88,8 @@ class CommonTestHelper(context: Context) {
fun syncSession(session: Session) { fun syncSession(session: Session) {
val lock = CountDownLatch(1) val lock = CountDownLatch(1)
session.open() GlobalScope.launch(Dispatchers.Main) { session.open() }
session.startSync(true) session.startSync(true)
val syncLiveData = runBlocking(Dispatchers.Main) { val syncLiveData = runBlocking(Dispatchers.Main) {
@@ -115,7 +117,7 @@ class CommonTestHelper(context: Context) {
*/ */
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> { fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages) val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(nbOfMessages) val latch = CountDownLatch(1)
val timelineListener = object : Timeline.Listener { val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) { override fun onTimelineFailure(throwable: Throwable) {
} }
@@ -126,7 +128,7 @@ class CommonTestHelper(context: Context) {
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) { override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val newMessages = snapshot val newMessages = snapshot
.filter { LocalEcho.isLocalEchoId(it.eventId).not() } .filter { it.root.sendState == SendState.SYNCED }
.filter { it.root.getClearType() == EventType.MESSAGE } .filter { it.root.getClearType() == EventType.MESSAGE }
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true } .filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
@@ -142,7 +144,8 @@ class CommonTestHelper(context: Context) {
for (i in 0 until nbOfMessages) { for (i in 0 until nbOfMessages) {
room.sendTextMessage(message + " #" + (i + 1)) room.sendTextMessage(message + " #" + (i + 1))
} }
await(latch) // Wait 3 second more per message
await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages)
timeline.removeListener(timelineListener) timeline.removeListener(timelineListener)
timeline.dispose() timeline.dispose()
@@ -182,9 +185,9 @@ class CommonTestHelper(context: Context) {
* @param testParams test params about the session * @param testParams test params about the session
* @return the session associated with the existing account * @return the session associated with the existing account
*/ */
private fun logIntoAccount(userId: String, fun logIntoAccount(userId: String,
password: String, password: String,
testParams: SessionTestParams): Session { testParams: SessionTestParams): Session {
val session = logAccountAndSync(userId, password, testParams) val session = logAccountAndSync(userId, password, testParams)
assertNotNull(session) assertNotNull(session)
return session return session
@@ -259,14 +262,81 @@ class CommonTestHelper(context: Context) {
return session return session
} }
/**
* Log into the account and expect an error
*
* @param userName the account username
* @param password the password
*/
fun logAccountWithError(userName: String,
password: String): Throwable {
val hs = createHomeServerConfig()
doSync<LoginFlowResult> {
matrix.authenticationService
.getLoginFlow(hs, it)
}
var requestFailure: Throwable? = null
waitWithLatch { latch ->
matrix.authenticationService
.getLoginWizard()
.login(userName, password, "myDevice", object : TestMatrixCallback<Session>(latch, onlySuccessful = false) {
override fun onFailure(failure: Throwable) {
requestFailure = failure
super.onFailure(failure)
}
})
}
assertNotNull(requestFailure)
return requestFailure!!
}
fun createEventListener(latch: CountDownLatch, predicate: (List<TimelineEvent>) -> Boolean): Timeline.Listener {
return object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
// noop
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
if (predicate(snapshot)) {
latch.countDown()
}
}
}
}
/** /**
* Await for a latch and ensure the result is true * Await for a latch and ensure the result is true
* *
* @param latch * @param latch
* @throws InterruptedException * @throws InterruptedException
*/ */
fun await(latch: CountDownLatch) { fun await(latch: CountDownLatch, timeout: Long? = TestConstants.timeOutMillis) {
assertTrue(latch.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)) assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
}
fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
GlobalScope.launch {
while (true) {
delay(1000)
if (condition()) {
latch.countDown()
return@launch
}
}
}
}
fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, block: (CountDownLatch) -> Unit) {
val latch = CountDownLatch(1)
block(latch)
await(latch, timeout)
} }
// Transform a method with a MatrixCallback to a synchronous method // Transform a method with a MatrixCallback to a synchronous method
@@ -299,3 +369,13 @@ class CommonTestHelper(context: Context) {
session.close() session.close()
} }
} }
fun List<TimelineEvent>.checkSendOrder(baseTextMessage: String, numberOfMessages: Int, startIndex: Int): Boolean {
return drop(startIndex)
.take(numberOfMessages)
.foldRightIndexed(true) { index, timelineEvent, acc ->
val body = timelineEvent.root.content.toModel<MessageContent>()?.body
val currentMessageSuffix = numberOfMessages - index
acc && (body == null || body.startsWith(baseTextMessage) && body.endsWith("#$currentMessageSuffix"))
}
}

View File

@@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
@@ -40,8 +41,6 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import java.util.HashMap
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
@@ -54,17 +53,19 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
/** /**
* @return alice session * @return alice session
*/ */
fun doE2ETestWithAliceInARoom(): CryptoTestData { fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
val roomId = mTestHelper.doSync<String> { val roomId = mTestHelper.doSync<String> {
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it) aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it)
} }
val room = aliceSession.getRoom(roomId)!! if (encryptedRoom) {
val room = aliceSession.getRoom(roomId)!!
mTestHelper.doSync<Unit> { mTestHelper.doSync<Unit> {
room.enableEncryption(callback = it) room.enableEncryption(callback = it)
}
} }
return CryptoTestData(aliceSession, roomId) return CryptoTestData(aliceSession, roomId)
@@ -73,8 +74,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
/** /**
* @return alice and bob sessions * @return alice and bob sessions
*/ */
fun doE2ETestWithAliceAndBobInARoom(): CryptoTestData { fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceInARoom() val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId val aliceRoomId = cryptoTestData.roomId
@@ -140,64 +141,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
* @return Alice, Bob and Sam session * @return Alice, Bob and Sam session
*/ */
fun doE2ETestWithAliceAndBobAndSamInARoom(): CryptoTestData { fun doE2ETestWithAliceAndBobAndSamInARoom(): CryptoTestData {
val statuses = HashMap<String, String>()
val cryptoTestData = doE2ETestWithAliceAndBobInARoom() val cryptoTestData = doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId val aliceRoomId = cryptoTestData.roomId
val room = aliceSession.getRoom(aliceRoomId)!! val room = aliceSession.getRoom(aliceRoomId)!!
val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) val samSession = createSamAccountAndInviteToTheRoom(room)
val lock1 = CountDownLatch(2)
// val samEventListener = object : MXEventListener() {
// override fun onNewRoom(roomId: String) {
// if (TextUtils.equals(roomId, aliceRoomId)) {
// if (!statuses.containsKey("onNewRoom")) {
// statuses["onNewRoom"] = "onNewRoom"
// lock1.countDown()
// }
// }
// }
// }
//
// samSession.dataHandler.addListener(samEventListener)
room.invite(samSession.myUserId, null, object : TestMatrixCallback<Unit>(lock1) {
override fun onSuccess(data: Unit) {
statuses["invite"] = "invite"
super.onSuccess(data)
}
})
mTestHelper.await(lock1)
assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))
// samSession.dataHandler.removeListener(samEventListener)
val lock2 = CountDownLatch(1)
samSession.joinRoom(aliceRoomId, null, object : TestMatrixCallback<Unit>(lock2) {
override fun onSuccess(data: Unit) {
statuses["joinRoom"] = "joinRoom"
super.onSuccess(data)
}
})
mTestHelper.await(lock2)
assertTrue(statuses.containsKey("joinRoom"))
// wait the initial sync // wait the initial sync
SystemClock.sleep(1000) SystemClock.sleep(1000)
// samSession.dataHandler.removeListener(samEventListener)
return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession) return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
} }
/**
* Create Sam account and invite him in the room. He will accept the invitation
* @Return Sam session
*/
fun createSamAccountAndInviteToTheRoom(room: Room): Session {
val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
mTestHelper.doSync<Unit> {
room.invite(samSession.myUserId, null, it)
}
mTestHelper.doSync<Unit> {
samSession.joinRoom(room.roomId, null, it)
}
return samSession
}
/** /**
* @return Alice and Bob sessions * @return Alice and Bob sessions
*/ */
@@ -273,7 +248,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
assertNotNull(eventWireContent.get("session_id")) assertNotNull(eventWireContent.get("session_id"))
assertNotNull(eventWireContent.get("sender_key")) assertNotNull(eventWireContent.get("sender_key"))
assertEquals(senderSession.sessionParams.credentials.deviceId, eventWireContent.get("device_id")) assertEquals(senderSession.sessionParams.deviceId, eventWireContent.get("device_id"))
assertNotNull(event.eventId) assertNotNull(event.eventId)
assertEquals(roomId, event.roomId) assertEquals(roomId, event.roomId)

View File

@@ -22,8 +22,8 @@ object TestConstants {
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080" const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
// Time out to use when waiting for server response. 10s // Time out to use when waiting for server response. 20s
private const val AWAIT_TIME_OUT_MILLIS = 10_000 private const val AWAIT_TIME_OUT_MILLIS = 20_000
// Time out to use when waiting for server response, when the debugger is connected. 10 minutes // Time out to use when waiting for server response, when the debugger is connected. 10 minutes
private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000 private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000

View File

@@ -20,6 +20,8 @@ import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
import im.vector.matrix.android.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import im.vector.matrix.android.internal.di.MoshiProvider
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlin.random.Random import kotlin.random.Random
@@ -31,6 +33,7 @@ internal class CryptoStoreHelper {
.name("test.realm") .name("test.realm")
.modules(RealmCryptoStoreModule()) .modules(RealmCryptoStoreModule())
.build(), .build(),
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
credentials = createCredential()) credentials = createCredential())
} }

View File

@@ -0,0 +1,247 @@
/*
* 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 im.vector.matrix.android.internal.crypto
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.TestConstants
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
import org.amshove.kluent.shouldBe
import org.junit.Assert
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.matrix.olm.OlmSession
import timber.log.Timber
import java.util.concurrent.CountDownLatch
/**
* Ref:
* - https://github.com/matrix-org/matrix-doc/pull/1719
* - https://matrix.org/docs/spec/client_server/latest#recovering-from-undecryptable-messages
* - https://github.com/matrix-org/matrix-js-sdk/pull/780
* - https://github.com/matrix-org/matrix-ios-sdk/pull/778
* - https://github.com/matrix-org/matrix-ios-sdk/pull/784
*/
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class UnwedgingTest : InstrumentedTest {
private lateinit var messagesReceivedByBob: List<TimelineEvent>
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Before
fun init() {
messagesReceivedByBob = emptyList()
}
/**
* - Alice & Bob in a e2e room
* - Alice sends a 1st message with a 1st megolm session
* - Store the olm session between A&B devices
* - Alice sends a 2nd message with a 2nd megolm session
* - Simulate Alice using a backup of her OS and make her crypto state like after the first message
* - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session
*
* What Bob must see:
* -> No issue with the 2 first messages
* -> The third event must fail to decrypt at first because Bob the olm session is wedged
* -> This is automatically fixed after SDKs restarted the olm session
*/
@Test
fun testUnwedging() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val bobSession = cryptoTestData.secondSession!!
val aliceCryptoStore = (aliceSession.cryptoService() as DefaultCryptoService).cryptoStoreForTesting
// bobSession.cryptoService().setWarnOnUnknownDevices(false)
// aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(20))
bobTimeline.start()
val bobFinalLatch = CountDownLatch(1)
val bobHasThreeDecryptedEventsListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
// noop
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val decryptedEventReceivedByBob = snapshot.filter { it.root.type == EventType.ENCRYPTED }
Timber.d("Bob can now decrypt ${decryptedEventReceivedByBob.size} messages")
if (decryptedEventReceivedByBob.size == 3) {
if (decryptedEventReceivedByBob[0].root.mCryptoError == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
bobFinalLatch.countDown()
}
}
}
}
bobTimeline.addListener(bobHasThreeDecryptedEventsListener)
var latch = CountDownLatch(1)
var bobEventsListener = createEventListener(latch, 1)
bobTimeline.addListener(bobEventsListener)
messagesReceivedByBob = emptyList()
// - Alice sends a 1st message with a 1st megolm session
roomFromAlicePOV.sendTextMessage("First message")
// Wait for the message to be received by Bob
mTestHelper.await(latch)
bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 1
val firstMessageSession = messagesReceivedByBob[0].root.content.toModel<EncryptedEventContent>()!!.sessionId!!
// - Store the olm session between A&B devices
// Let us pickle our session with bob here so we can later unpickle it
// and wedge our session.
val sessionIdsForBob = aliceCryptoStore.getDeviceSessionIds(bobSession.cryptoService().getMyDevice().identityKey()!!)
sessionIdsForBob!!.size shouldBe 1
val olmSession = aliceCryptoStore.getDeviceSession(sessionIdsForBob.first(), bobSession.cryptoService().getMyDevice().identityKey()!!)!!
val oldSession = serializeForRealm(olmSession.olmSession)
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
Thread.sleep(6_000)
latch = CountDownLatch(1)
bobEventsListener = createEventListener(latch, 2)
bobTimeline.addListener(bobEventsListener)
messagesReceivedByBob = emptyList()
Timber.i("## CRYPTO | testUnwedging: Alice sends a 2nd message with a 2nd megolm session")
// - Alice sends a 2nd message with a 2nd megolm session
roomFromAlicePOV.sendTextMessage("Second message")
// Wait for the message to be received by Bob
mTestHelper.await(latch)
bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 2
// Session should have changed
val secondMessageSession = messagesReceivedByBob[0].root.content.toModel<EncryptedEventContent>()!!.sessionId!!
Assert.assertNotEquals(firstMessageSession, secondMessageSession)
// Let us wedge the session now. Set crypto state like after the first message
Timber.i("## CRYPTO | testUnwedging: wedge the session now. Set crypto state like after the first message")
aliceCryptoStore.storeSession(OlmSessionWrapper(deserializeFromRealm<OlmSession>(oldSession)!!), bobSession.cryptoService().getMyDevice().identityKey()!!)
Thread.sleep(6_000)
// Force new session, and key share
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
// Wait for the message to be received by Bob
mTestHelper.waitWithLatch {
bobEventsListener = createEventListener(it, 3)
bobTimeline.addListener(bobEventsListener)
messagesReceivedByBob = emptyList()
Timber.i("## CRYPTO | testUnwedging: Alice sends a 3rd message with a 3rd megolm session but a wedged olm session")
// - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session
roomFromAlicePOV.sendTextMessage("Third message")
// Bob should not be able to decrypt, because the session key could not be sent
}
bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 3
val thirdMessageSession = messagesReceivedByBob[0].root.content.toModel<EncryptedEventContent>()!!.sessionId!!
Timber.i("## CRYPTO | testUnwedging: third message session ID $thirdMessageSession")
Assert.assertNotEquals(secondMessageSession, thirdMessageSession)
Assert.assertEquals(EventType.ENCRYPTED, messagesReceivedByBob[0].root.getClearType())
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType())
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType())
// Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged
mTestHelper.await(bobFinalLatch)
bobTimeline.removeListener(bobHasThreeDecryptedEventsListener)
// It's a trick to force key request on fail to decrypt
mTestHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = bobSession.myUserId,
password = TestConstants.PASSWORD
), it)
}
// Wait until we received back the key
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
// we should get back the key and be able to decrypt
val result = tryThis {
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
}
Timber.i("## CRYPTO | testUnwedging: decrypt result ${result?.clearEvent}")
result != null
}
}
bobTimeline.dispose()
cryptoTestData.cleanUp(mTestHelper)
}
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
return object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
// noop
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
messagesReceivedByBob = snapshot.filter { it.root.type == EventType.ENCRYPTED }
if (messagesReceivedByBob.size == expectedNumberOfMessages) {
latch.countDown()
}
}
}
}
}

View File

@@ -122,7 +122,7 @@ class XSigningTest : InstrumentedTest {
// We will want to test that in alice POV, this new device would be trusted by cross signing // We will want to test that in alice POV, this new device would be trusted by cross signing
val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true)) val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true))
val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId!! val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
// Check that bob first session sees the new login // Check that bob first session sees the new login
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {

View File

@@ -0,0 +1,289 @@
/*
* 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 im.vector.matrix.android.internal.crypto.gossiping
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.SasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants
import im.vector.matrix.android.internal.crypto.GossipingRequestState
import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertTrue
import junit.framework.TestCase.fail
import org.junit.Assert
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class KeyShareTests : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
@Test
fun test_DoNotSelfShareIfNotTrusted() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
// Create an encrypted room and add a message
val roomId = mTestHelper.doSync<String> {
aliceSession.createRoom(
CreateRoomParams(RoomDirectoryVisibility.PRIVATE).enableEncryptionWithAlgorithm(true),
it
)
}
val room = aliceSession.getRoom(roomId)
assertNotNull(room)
Thread.sleep(4_000)
assertTrue(room?.isEncrypted() == true)
val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
// Open a new sessionx
val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
val receivedEvent = roomSecondSessionPOV?.getTimeLineEvent(sentEventId)
assertNotNull(receivedEvent)
assert(receivedEvent!!.isEncrypted())
try {
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
fail("should fail")
} catch (failure: Throwable) {
}
val outgoingRequestBefore = aliceSession2.cryptoService().getOutgoingRoomKeyRequest()
// Try to request
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
val waitLatch = CountDownLatch(1)
val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
var outGoingRequestId: String? = null
mTestHelper.retryPeriodicallyWithLatch(waitLatch) {
aliceSession2.cryptoService().getOutgoingRoomKeyRequest()
.filter { req ->
// filter out request that was known before
!outgoingRequestBefore.any { req.requestId == it.requestId }
}
.let {
val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
outGoingRequestId = outgoing?.requestId
outgoing != null
}
}
mTestHelper.await(waitLatch)
Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequest()
// We should have a new request
Assert.assertTrue(outgoingRequestAfter.size > outgoingRequestBefore.size)
Assert.assertNotNull(outgoingRequestAfter.first { it.sessionId == eventMegolmSessionId })
// The first session should see an incoming request
// the request should be refused, because the device is not trusted
mTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) {
// DEBUG LOGS
aliceSession.cryptoService().getIncomingRoomKeyRequest().let {
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
Log.v("TEST", "=========================")
it.forEach { keyRequest ->
Log.v("TEST", "[ts${keyRequest.localCreationTimestamp}] requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId} is ${keyRequest.state}")
}
Log.v("TEST", "=========================")
}
val incoming = aliceSession.cryptoService().getIncomingRoomKeyRequest().firstOrNull { it.requestId == outGoingRequestId }
incoming?.state == GossipingRequestState.REJECTED
}
}
try {
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
fail("should fail")
} catch (failure: Throwable) {
}
// Mark the device as trusted
aliceSession.cryptoService().setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true), aliceSession.myUserId,
aliceSession2.sessionParams.deviceId ?: "")
// Re request
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
mTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession.cryptoService().getIncomingRoomKeyRequest().let {
Log.v("TEST", "Incoming request Session 1")
Log.v("TEST", "=========================")
it.forEach {
Log.v("TEST", "requestId ${it.requestId}, for sessionId ${it.requestBody?.sessionId} is ${it.state}")
}
Log.v("TEST", "=========================")
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == GossipingRequestState.ACCEPTED }
}
}
}
Thread.sleep(6_000)
mTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession2.cryptoService().getOutgoingRoomKeyRequest().let {
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
}
}
}
try {
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
} catch (failure: Throwable) {
fail("should have been able to decrypt")
}
mTestHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(aliceSession2)
}
@Test
fun test_ShareSSSSSecret() {
val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> {
aliceSession1.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession1.myUserId,
password = TestConstants.PASSWORD
), it)
}
// Also bootstrap keybackup on first session
val creationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
}
val version = mTestHelper.doSync<KeysVersion> {
aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
}
// Save it for gossiping
aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
val aliceSession2 = mTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true))
val aliceVerificationService1 = aliceSession1.cryptoService().verificationService()
val aliceVerificationService2 = aliceSession2.cryptoService().verificationService()
// force keys download
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it)
}
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it)
}
var session1ShortCode: String? = null
var session2ShortCode: String? = null
aliceVerificationService1.addListener(object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
Log.d("#TEST", "AA: tx incoming?:${tx.isIncoming} state ${tx.state}")
if (tx is SasVerificationTransaction) {
if (tx.state == VerificationTxState.OnStarted) {
(tx as IncomingSasVerificationTransaction).performAccept()
}
if (tx.state == VerificationTxState.ShortCodeReady) {
session1ShortCode = tx.getDecimalCodeRepresentation()
tx.userHasVerifiedShortCode()
}
}
}
})
aliceVerificationService2.addListener(object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
Log.d("#TEST", "BB: tx incoming?:${tx.isIncoming} state ${tx.state}")
if (tx is SasVerificationTransaction) {
if (tx.state == VerificationTxState.ShortCodeReady) {
session2ShortCode = tx.getDecimalCodeRepresentation()
tx.userHasVerifiedShortCode()
}
}
}
})
val txId: String = "m.testVerif12"
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
?: "", txId)
mTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true
}
}
assertNotNull(session1ShortCode)
Log.d("#TEST", "session1ShortCode: $session1ShortCode")
assertNotNull(session2ShortCode)
Log.d("#TEST", "session2ShortCode: $session2ShortCode")
assertEquals(session1ShortCode, session2ShortCode)
// SSK and USK private keys should have been shared
mTestHelper.waitWithLatch(60_000) { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) {
Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
aliceSession2.cryptoService().crossSigningService().canCrossSign()
}
}
// Test that key backup key has been shared to
mTestHelper.waitWithLatch(60_000) { latch ->
val keysBackupService = aliceSession2.cryptoService().keysBackupService()
mTestHelper.retryPeriodicallyWithLatch(latch) {
Log.d("#TEST", "Recovery :${ keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}")
keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey
}
}
}
}

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 im.vector.matrix.android.internal.crypto.keysbackup
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestData
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
/**
* Data class to store result of [KeysBackupTestHelper.createKeysBackupScenarioWithPassword]
*/
data class KeysBackupScenarioData(val cryptoTestData: CryptoTestData,
val aliceKeys: List<OlmInboundGroupSessionWrapper2>,
val prepareKeysBackupDataResult: PrepareKeysBackupDataResult,
val aliceSession2: Session) {
fun cleanUp(testHelper: CommonTestHelper) {
cryptoTestData.cleanUp(testHelper)
testHelper.signOutAndClose(aliceSession2)
}
}

View File

@@ -20,28 +20,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.listeners.ProgressListener import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.api.listeners.StepProgressListener import im.vector.matrix.android.api.listeners.StepProgressListener
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
import im.vector.matrix.android.common.CommonTestHelper import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestData
import im.vector.matrix.android.common.CryptoTestHelper import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants import im.vector.matrix.android.common.TestConstants
import im.vector.matrix.android.common.TestMatrixCallback import im.vector.matrix.android.common.TestMatrixCallback
import im.vector.matrix.android.common.assertDictEquals
import im.vector.matrix.android.common.assertListEquals
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import im.vector.matrix.android.internal.crypto.MegolmSessionData
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
@@ -62,9 +53,7 @@ class KeysBackupTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
private val mKeysBackupTestHelper = KeysBackupTestHelper(mTestHelper, mCryptoTestHelper)
private val defaultSessionParams = SessionTestParams(withInitialSync = false)
private val defaultSessionParamsWithInitialSync = SessionTestParams(withInitialSync = true)
/** /**
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys * - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
@@ -111,7 +100,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun prepareKeysBackupVersionTest() { fun prepareKeysBackupVersionTest() {
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
assertNotNull(bobSession.cryptoService().keysBackupService()) assertNotNull(bobSession.cryptoService().keysBackupService())
@@ -140,7 +129,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun createKeysBackupVersionTest() { fun createKeysBackupVersionTest() {
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
val keysBackup = bobSession.cryptoService().keysBackupService() val keysBackup = bobSession.cryptoService().keysBackupService()
@@ -183,7 +172,7 @@ class KeysBackupTest : InstrumentedTest {
val stateObserver = StateObserver(keysBackup, latch, 5) val stateObserver = StateObserver(keysBackup, latch, 5)
prepareAndCreateKeysBackupData(keysBackup) mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
mTestHelper.await(latch) mTestHelper.await(latch)
@@ -217,7 +206,7 @@ class KeysBackupTest : InstrumentedTest {
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
prepareAndCreateKeysBackupData(keysBackup) mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Check that backupAllGroupSessions returns valid data // Check that backupAllGroupSessions returns valid data
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false) val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
@@ -264,7 +253,7 @@ class KeysBackupTest : InstrumentedTest {
// - Pick a megolm key // - Pick a megolm key
val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0] val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0]
val keyBackupCreationInfo = prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo
// - Check encryptGroupSession() returns stg // - Check encryptGroupSession() returns stg
val keyBackupData = keysBackup.encryptGroupSession(session) val keyBackupData = keysBackup.encryptGroupSession(session)
@@ -282,7 +271,7 @@ class KeysBackupTest : InstrumentedTest {
decryption!!) decryption!!)
assertNotNull(sessionData) assertNotNull(sessionData)
// - Compare the decrypted megolm key with the original one // - Compare the decrypted megolm key with the original one
assertKeysEquals(session.exportKeys(), sessionData) mKeysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(mTestHelper)
@@ -296,7 +285,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun restoreKeysBackupTest() { fun restoreKeysBackupTest() {
val testData = createKeysBackupScenarioWithPassword(null) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Restore the e2e backup from the homeserver // - Restore the e2e backup from the homeserver
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> { val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
@@ -309,7 +298,7 @@ class KeysBackupTest : InstrumentedTest {
) )
} }
checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
testData.cleanUp(mTestHelper) testData.cleanUp(mTestHelper)
} }
@@ -326,46 +315,46 @@ class KeysBackupTest : InstrumentedTest {
* - Restore must be successful * - Restore must be successful
* - *** There must be no more pending key share requests * - *** There must be no more pending key share requests
*/ */
@Test // @Test
fun restoreKeysBackupAndKeyShareRequestTest() { // fun restoreKeysBackupAndKeyShareRequestTest() {
fail("Check with Valere for this test. I think we do not send key share request") // fail("Check with Valere for this test. I think we do not send key share request")
//
val testData = createKeysBackupScenarioWithPassword(null) // val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
//
// - Check the SDK sent key share requests // // - Check the SDK sent key share requests
val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store // val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
val unsentRequest = cryptoStore2 // val unsentRequest = cryptoStore2
.getOutgoingRoomKeyRequestByState(setOf(OutgoingRoomKeyRequest.RequestState.UNSENT)) // .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.UNSENT))
val sentRequest = cryptoStore2 // val sentRequest = cryptoStore2
.getOutgoingRoomKeyRequestByState(setOf(OutgoingRoomKeyRequest.RequestState.SENT)) // .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.SENT))
//
// Request is either sent or unsent // // Request is either sent or unsent
assertTrue(unsentRequest != null || sentRequest != null) // assertTrue(unsentRequest != null || sentRequest != null)
//
// - Restore the e2e backup from the homeserver // // - Restore the e2e backup from the homeserver
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> { // val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, // testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, // testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
null, // null,
null, // null,
null, // null,
it // it
) // )
} // }
//
checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) // mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
//
// - There must be no more pending key share requests // // - There must be no more pending key share requests
val unsentRequestAfterRestoration = cryptoStore2 // val unsentRequestAfterRestoration = cryptoStore2
.getOutgoingRoomKeyRequestByState(setOf(OutgoingRoomKeyRequest.RequestState.UNSENT)) // .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.UNSENT))
val sentRequestAfterRestoration = cryptoStore2 // val sentRequestAfterRestoration = cryptoStore2
.getOutgoingRoomKeyRequestByState(setOf(OutgoingRoomKeyRequest.RequestState.SENT)) // .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.SENT))
//
// Request is either sent or unsent // // Request is either sent or unsent
assertTrue(unsentRequestAfterRestoration == null && sentRequestAfterRestoration == null) // assertTrue(unsentRequestAfterRestoration == null && sentRequestAfterRestoration == null)
//
testData.cleanUp(mTestHelper) // testData.cleanUp(mTestHelper)
} // }
/** /**
* - Do an e2e backup to the homeserver with a recovery key * - Do an e2e backup to the homeserver with a recovery key
@@ -381,7 +370,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionTest() { fun trustKeyBackupVersionTest() {
// - Do an e2e backup to the homeserver with a recovery key // - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device // - And log Alice on a new device
val testData = createKeysBackupScenarioWithPassword(null) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -400,7 +389,7 @@ class KeysBackupTest : InstrumentedTest {
} }
// Wait for backup state to be ReadyToBackUp // Wait for backup state to be ReadyToBackUp
waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
// - Backup must be enabled on the new device, on the same version // - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
@@ -440,7 +429,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionWithRecoveryKeyTest() { fun trustKeyBackupVersionWithRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key // - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device // - And log Alice on a new device
val testData = createKeysBackupScenarioWithPassword(null) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -459,7 +448,7 @@ class KeysBackupTest : InstrumentedTest {
} }
// Wait for backup state to be ReadyToBackUp // Wait for backup state to be ReadyToBackUp
waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
// - Backup must be enabled on the new device, on the same version // - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
@@ -497,7 +486,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() { fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key // - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device // - And log Alice on a new device
val testData = createKeysBackupScenarioWithPassword(null) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -540,7 +529,7 @@ class KeysBackupTest : InstrumentedTest {
// - Do an e2e backup to the homeserver with a password // - Do an e2e backup to the homeserver with a password
// - And log Alice on a new device // - And log Alice on a new device
val testData = createKeysBackupScenarioWithPassword(password) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -559,7 +548,7 @@ class KeysBackupTest : InstrumentedTest {
} }
// Wait for backup state to be ReadyToBackUp // Wait for backup state to be ReadyToBackUp
waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
// - Backup must be enabled on the new device, on the same version // - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
@@ -600,7 +589,7 @@ class KeysBackupTest : InstrumentedTest {
// - Do an e2e backup to the homeserver with a password // - Do an e2e backup to the homeserver with a password
// - And log Alice on a new device // - And log Alice on a new device
val testData = createKeysBackupScenarioWithPassword(password) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -635,7 +624,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun restoreKeysBackupWithAWrongRecoveryKeyTest() { fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
val testData = createKeysBackupScenarioWithPassword(null) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Try to restore the e2e backup with a wrong recovery key // - Try to restore the e2e backup with a wrong recovery key
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
@@ -670,7 +659,7 @@ class KeysBackupTest : InstrumentedTest {
fun testBackupWithPassword() { fun testBackupWithPassword() {
val password = "password" val password = "password"
val testData = createKeysBackupScenarioWithPassword(password) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Restore the e2e backup with the password // - Restore the e2e backup with the password
val steps = ArrayList<StepProgressListener.Step>() val steps = ArrayList<StepProgressListener.Step>()
@@ -710,7 +699,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress) assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress)
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress) assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
testData.cleanUp(mTestHelper) testData.cleanUp(mTestHelper)
} }
@@ -726,7 +715,7 @@ class KeysBackupTest : InstrumentedTest {
val password = "password" val password = "password"
val wrongPassword = "passw0rd" val wrongPassword = "passw0rd"
val testData = createKeysBackupScenarioWithPassword(password) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Try to restore the e2e backup with a wrong password // - Try to restore the e2e backup with a wrong password
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
@@ -761,7 +750,7 @@ class KeysBackupTest : InstrumentedTest {
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() { fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
val password = "password" val password = "password"
val testData = createKeysBackupScenarioWithPassword(password) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Restore the e2e backup with the recovery key. // - Restore the e2e backup with the recovery key.
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> { val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
@@ -774,7 +763,7 @@ class KeysBackupTest : InstrumentedTest {
) )
} }
checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
testData.cleanUp(mTestHelper) testData.cleanUp(mTestHelper)
} }
@@ -787,7 +776,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() { fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
val testData = createKeysBackupScenarioWithPassword(null) val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Try to restore the e2e backup with a password // - Try to restore the e2e backup with a password
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
@@ -826,7 +815,7 @@ class KeysBackupTest : InstrumentedTest {
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
// - Do an e2e backup to the homeserver // - Do an e2e backup to the homeserver
prepareAndCreateKeysBackupData(keysBackup) mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Get key backup version from the home server // Get key backup version from the home server
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> { val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> {
@@ -846,7 +835,7 @@ class KeysBackupTest : InstrumentedTest {
assertTrue(signature.valid) assertTrue(signature.valid)
assertNotNull(signature.device) assertNotNull(signature.device)
assertEquals(cryptoTestData.firstSession.cryptoService().getMyDevice().deviceId, signature.deviceId) assertEquals(cryptoTestData.firstSession.cryptoService().getMyDevice().deviceId, signature.deviceId)
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.credentials.deviceId) assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(mTestHelper)
@@ -871,13 +860,13 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
val keyBackupCreationInfo = prepareAndCreateKeysBackupData(keysBackup) val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
// - Restart alice session // - Restart alice session
// - Log Alice on a new device // - Log Alice on a new device
val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, defaultSessionParamsWithInitialSync) val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(mTestHelper)
@@ -951,7 +940,7 @@ class KeysBackupTest : InstrumentedTest {
}) })
// - Make alice back up her keys to her homeserver // - Make alice back up her keys to her homeserver
prepareAndCreateKeysBackupData(keysBackup) mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
@@ -1001,19 +990,19 @@ class KeysBackupTest : InstrumentedTest {
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
// - Make alice back up her keys to her homeserver // - Make alice back up her keys to her homeserver
prepareAndCreateKeysBackupData(keysBackup) mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Wait for keys backup to finish by asking again to backup keys. // Wait for keys backup to finish by asking again to backup keys.
mTestHelper.doSync<Unit> { mTestHelper.doSync<Unit> {
keysBackup.backupAllGroupSessions(null, it) keysBackup.backupAllGroupSessions(null, it)
} }
val oldDeviceId = cryptoTestData.firstSession.sessionParams.credentials.deviceId!! val oldDeviceId = cryptoTestData.firstSession.sessionParams.deviceId!!
val oldKeyBackupVersion = keysBackup.currentBackupVersion val oldKeyBackupVersion = keysBackup.currentBackupVersion
val aliceUserId = cryptoTestData.firstSession.myUserId val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device // - Log Alice on a new device
val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync) val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
// - Post a message to have a new megolm session // - Post a message to have a new megolm session
aliceSession2.cryptoService().setWarnOnUnknownDevices(false) aliceSession2.cryptoService().setWarnOnUnknownDevices(false)
@@ -1094,7 +1083,7 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
val keyBackupCreationInfo = prepareAndCreateKeysBackupData(keysBackup) val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
@@ -1107,169 +1096,4 @@ class KeysBackupTest : InstrumentedTest {
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(mTestHelper)
} }
/* ==========================================================================================
* Private
* ========================================================================================== */
/**
* As KeysBackup is doing asynchronous call to update its internal state, this method help to wait for the
* KeysBackup object to be in the specified state
*/
private fun waitForKeysBackupToBeInState(session: Session, state: KeysBackupState) {
// If already in the wanted state, return
if (session.cryptoService().keysBackupService().state == state) {
return
}
// Else observe state changes
val latch = CountDownLatch(1)
session.cryptoService().keysBackupService().addListener(object : KeysBackupStateListener {
override fun onStateChange(newState: KeysBackupState) {
if (newState == state) {
session.cryptoService().keysBackupService().removeListener(this)
latch.countDown()
}
}
})
mTestHelper.await(latch)
}
private data class PrepareKeysBackupDataResult(val megolmBackupCreationInfo: MegolmBackupCreationInfo,
val version: String)
private fun prepareAndCreateKeysBackupData(keysBackup: KeysBackupService,
password: String? = null): PrepareKeysBackupDataResult {
val stateObserver = StateObserver(keysBackup)
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
keysBackup.prepareKeysBackupVersion(password, null, it)
}
assertNotNull(megolmBackupCreationInfo)
assertFalse(keysBackup.isEnabled)
// Create the version
val keysVersion = mTestHelper.doSync<KeysVersion> {
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
}
assertNotNull(keysVersion.version)
// Backup must be enable now
assertTrue(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null)
return PrepareKeysBackupDataResult(megolmBackupCreationInfo, keysVersion.version!!)
}
private fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) {
assertNotNull(keys1)
assertNotNull(keys2)
assertEquals(keys1?.algorithm, keys2?.algorithm)
assertEquals(keys1?.roomId, keys2?.roomId)
// No need to compare the shortcut
// assertEquals(keys1?.sender_claimed_ed25519_key, keys2?.sender_claimed_ed25519_key)
assertEquals(keys1?.senderKey, keys2?.senderKey)
assertEquals(keys1?.sessionId, keys2?.sessionId)
assertEquals(keys1?.sessionKey, keys2?.sessionKey)
assertListEquals(keys1?.forwardingCurve25519KeyChain, keys2?.forwardingCurve25519KeyChain)
assertDictEquals(keys1?.senderClaimedKeys, keys2?.senderClaimedKeys)
}
/**
* Data class to store result of [createKeysBackupScenarioWithPassword]
*/
private data class KeysBackupScenarioData(val cryptoTestData: CryptoTestData,
val aliceKeys: List<OlmInboundGroupSessionWrapper>,
val prepareKeysBackupDataResult: PrepareKeysBackupDataResult,
val aliceSession2: Session) {
fun cleanUp(testHelper: CommonTestHelper) {
cryptoTestData.cleanUp(testHelper)
testHelper.signOutAndClose(aliceSession2)
}
}
/**
* Common initial condition
* - Do an e2e backup to the homeserver
* - Log Alice on a new device, and wait for its keysBackup object to be ready (in state NotTrusted)
*
* @param password optional password
*/
private fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup)
val aliceKeys = cryptoStore.inboundGroupSessionsToBackup(100)
// - Do an e2e backup to the homeserver
val prepareKeysBackupDataResult = prepareAndCreateKeysBackupData(keysBackup, password)
var lastProgress = 0
var lastTotal = 0
mTestHelper.doSync<Unit> {
keysBackup.backupAllGroupSessions(object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
lastProgress = progress
lastTotal = total
}
}, it)
}
assertEquals(2, lastProgress)
assertEquals(2, lastTotal)
val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device
val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync)
// Test check: aliceSession2 has no keys at login
assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
// Wait for backup state to be NotTrusted
waitForKeysBackupToBeInState(aliceSession2, KeysBackupState.NotTrusted)
stateObserver.stopAndCheckStates(null)
return KeysBackupScenarioData(cryptoTestData,
aliceKeys,
prepareKeysBackupDataResult,
aliceSession2)
}
/**
* Common restore success check after [createKeysBackupScenarioWithPassword]:
* - Imported keys number must be correct
* - The new device must have the same count of megolm keys
* - Alice must have the same keys on both devices
*/
private fun checkRestoreSuccess(testData: KeysBackupScenarioData,
total: Int,
imported: Int) {
// - Imported keys number must be correct
assertEquals(testData.aliceKeys.size, total)
assertEquals(total, imported)
// - The new device must have the same count of megolm keys
assertEquals(testData.aliceKeys.size, testData.aliceSession2.cryptoService().inboundGroupSessionsCount(false))
// - Alice must have the same keys on both devices
for (aliceKey1 in testData.aliceKeys) {
val aliceKey2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
.getInboundGroupSession(aliceKey1.olmInboundGroupSession!!.sessionIdentifier(), aliceKey1.senderKey!!)
assertNotNull(aliceKey2)
assertKeysEquals(aliceKey1.exportKeys(), aliceKey2!!.exportKeys())
}
}
} }

View File

@@ -0,0 +1,24 @@
/*
* 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 im.vector.matrix.android.internal.crypto.keysbackup
import im.vector.matrix.android.common.SessionTestParams
object KeysBackupTestConstants {
val defaultSessionParams = SessionTestParams(withInitialSync = false)
val defaultSessionParamsWithInitialSync = SessionTestParams(withInitialSync = true)
}

View File

@@ -0,0 +1,182 @@
/*
* 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 im.vector.matrix.android.internal.crypto.keysbackup
import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.assertDictEquals
import im.vector.matrix.android.common.assertListEquals
import im.vector.matrix.android.internal.crypto.MegolmSessionData
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
import org.junit.Assert
import java.util.concurrent.CountDownLatch
class KeysBackupTestHelper(
private val mTestHelper: CommonTestHelper,
private val mCryptoTestHelper: CryptoTestHelper) {
/**
* Common initial condition
* - Do an e2e backup to the homeserver
* - Log Alice on a new device, and wait for its keysBackup object to be ready (in state NotTrusted)
*
* @param password optional password
*/
fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup)
val aliceKeys = cryptoStore.inboundGroupSessionsToBackup(100)
// - Do an e2e backup to the homeserver
val prepareKeysBackupDataResult = prepareAndCreateKeysBackupData(keysBackup, password)
var lastProgress = 0
var lastTotal = 0
mTestHelper.doSync<Unit> {
keysBackup.backupAllGroupSessions(object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
lastProgress = progress
lastTotal = total
}
}, it)
}
Assert.assertEquals(2, lastProgress)
Assert.assertEquals(2, lastTotal)
val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device
val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
// Test check: aliceSession2 has no keys at login
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
// Wait for backup state to be NotTrusted
waitForKeysBackupToBeInState(aliceSession2, KeysBackupState.NotTrusted)
stateObserver.stopAndCheckStates(null)
return KeysBackupScenarioData(cryptoTestData,
aliceKeys,
prepareKeysBackupDataResult,
aliceSession2)
}
fun prepareAndCreateKeysBackupData(keysBackup: KeysBackupService,
password: String? = null): PrepareKeysBackupDataResult {
val stateObserver = StateObserver(keysBackup)
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> {
keysBackup.prepareKeysBackupVersion(password, null, it)
}
Assert.assertNotNull(megolmBackupCreationInfo)
Assert.assertFalse(keysBackup.isEnabled)
// Create the version
val keysVersion = mTestHelper.doSync<KeysVersion> {
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
}
Assert.assertNotNull(keysVersion.version)
// Backup must be enable now
Assert.assertTrue(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null)
return PrepareKeysBackupDataResult(megolmBackupCreationInfo, keysVersion.version!!)
}
/**
* As KeysBackup is doing asynchronous call to update its internal state, this method help to wait for the
* KeysBackup object to be in the specified state
*/
fun waitForKeysBackupToBeInState(session: Session, state: KeysBackupState) {
// If already in the wanted state, return
if (session.cryptoService().keysBackupService().state == state) {
return
}
// Else observe state changes
val latch = CountDownLatch(1)
session.cryptoService().keysBackupService().addListener(object : KeysBackupStateListener {
override fun onStateChange(newState: KeysBackupState) {
if (newState == state) {
session.cryptoService().keysBackupService().removeListener(this)
latch.countDown()
}
}
})
mTestHelper.await(latch)
}
fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) {
Assert.assertNotNull(keys1)
Assert.assertNotNull(keys2)
Assert.assertEquals(keys1?.algorithm, keys2?.algorithm)
Assert.assertEquals(keys1?.roomId, keys2?.roomId)
// No need to compare the shortcut
// assertEquals(keys1?.sender_claimed_ed25519_key, keys2?.sender_claimed_ed25519_key)
Assert.assertEquals(keys1?.senderKey, keys2?.senderKey)
Assert.assertEquals(keys1?.sessionId, keys2?.sessionId)
Assert.assertEquals(keys1?.sessionKey, keys2?.sessionKey)
assertListEquals(keys1?.forwardingCurve25519KeyChain, keys2?.forwardingCurve25519KeyChain)
assertDictEquals(keys1?.senderClaimedKeys, keys2?.senderClaimedKeys)
}
/**
* Common restore success check after [KeysBackupTestHelper.createKeysBackupScenarioWithPassword]:
* - Imported keys number must be correct
* - The new device must have the same count of megolm keys
* - Alice must have the same keys on both devices
*/
fun checkRestoreSuccess(testData: KeysBackupScenarioData,
total: Int,
imported: Int) {
// - Imported keys number must be correct
Assert.assertEquals(testData.aliceKeys.size, total)
Assert.assertEquals(total, imported)
// - The new device must have the same count of megolm keys
Assert.assertEquals(testData.aliceKeys.size, testData.aliceSession2.cryptoService().inboundGroupSessionsCount(false))
// - Alice must have the same keys on both devices
for (aliceKey1 in testData.aliceKeys) {
val aliceKey2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
.getInboundGroupSession(aliceKey1.olmInboundGroupSession!!.sessionIdentifier(), aliceKey1.senderKey!!)
Assert.assertNotNull(aliceKey2)
assertKeysEquals(aliceKey1.exportKeys(), aliceKey2!!.exportKeys())
}
}
}

View File

@@ -0,0 +1,22 @@
/*
* 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 im.vector.matrix.android.internal.crypto.keysbackup
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
data class PrepareKeysBackupDataResult(val megolmBackupCreationInfo: MegolmBackupCreationInfo,
val version: String)

View File

@@ -71,7 +71,7 @@ class QuadSTests : InstrumentedTest {
val TEST_KEY_ID = "my.test.Key" val TEST_KEY_ID = "my.test.Key"
mTestHelper.doSync<SsssKeyCreationInfo> { mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it) quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner, it)
} }
// Assert Account data is updated // Assert Account data is updated
@@ -177,7 +177,7 @@ class QuadSTests : InstrumentedTest {
val TEST_KEY_ID = "my.test.Key" val TEST_KEY_ID = "my.test.Key"
mTestHelper.doSync<SsssKeyCreationInfo> { mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it) quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner, it)
} }
// Test that we don't need to wait for an account data sync to access directly the keyid from DB // Test that we don't need to wait for an account data sync to access directly the keyid from DB
@@ -322,7 +322,7 @@ class QuadSTests : InstrumentedTest {
val quadS = session.sharedSecretStorageService val quadS = session.sharedSecretStorageService
val creationInfo = mTestHelper.doSync<SsssKeyCreationInfo> { val creationInfo = mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKey(keyId, keyId, emptyKeySigner, it) quadS.generateKey(keyId, null, keyId, emptyKeySigner, it)
} }
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")

View File

@@ -16,6 +16,7 @@
package im.vector.matrix.android.internal.crypto.verification package im.vector.matrix.android.internal.crypto.verification
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
@@ -23,6 +24,7 @@ import im.vector.matrix.android.api.session.crypto.verification.CancelCode
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.OutgoingSasVerificationTransaction import im.vector.matrix.android.api.session.crypto.verification.OutgoingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.SasMode import im.vector.matrix.android.api.session.crypto.verification.SasMode
import im.vector.matrix.android.api.session.crypto.verification.SasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
import im.vector.matrix.android.api.session.crypto.verification.VerificationService import im.vector.matrix.android.api.session.crypto.verification.VerificationService
import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction
@@ -355,6 +357,7 @@ class SASTest : InstrumentedTest {
val aliceAcceptedLatch = CountDownLatch(1) val aliceAcceptedLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.Listener { val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
Log.v("TEST", "== aliceTx state ${tx.state} => ${(tx as? OutgoingSasVerificationTransaction)?.uxState}")
if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) { if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) {
val at = tx as SASDefaultVerificationTransaction val at = tx as SASDefaultVerificationTransaction
accepted = at.accepted accepted = at.accepted
@@ -367,7 +370,9 @@ class SASTest : InstrumentedTest {
val bobListener = object : VerificationService.Listener { val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
Log.v("TEST", "== bobTx state ${tx.state} => ${(tx as? IncomingSasVerificationTransaction)?.uxState}")
if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) {
bobVerificationService.removeListener(this)
val at = tx as IncomingSasVerificationTransaction val at = tx as IncomingSasVerificationTransaction
at.performAccept() at.performAccept()
} }
@@ -463,14 +468,19 @@ class SASTest : InstrumentedTest {
val aliceSASLatch = CountDownLatch(1) val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.Listener { val aliceListener = object : VerificationService.Listener {
var matchOnce = true
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState val uxState = (tx as OutgoingSasVerificationTransaction).uxState
Log.v("TEST", "== aliceState ${uxState.name}")
when (uxState) { when (uxState) {
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> { OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
tx.userHasVerifiedShortCode() tx.userHasVerifiedShortCode()
} }
OutgoingSasVerificationTransaction.UxState.VERIFIED -> { OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
aliceSASLatch.countDown() if (matchOnce) {
matchOnce = false
aliceSASLatch.countDown()
}
} }
else -> Unit else -> Unit
} }
@@ -480,14 +490,23 @@ class SASTest : InstrumentedTest {
val bobSASLatch = CountDownLatch(1) val bobSASLatch = CountDownLatch(1)
val bobListener = object : VerificationService.Listener { val bobListener = object : VerificationService.Listener {
var acceptOnce = true
var matchOnce = true
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState val uxState = (tx as IncomingSasVerificationTransaction).uxState
Log.v("TEST", "== bobState ${uxState.name}")
when (uxState) { when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> { IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
tx.performAccept() if (acceptOnce) {
acceptOnce = false
tx.performAccept()
}
} }
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> { IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
tx.userHasVerifiedShortCode() if (matchOnce) {
matchOnce = false
tx.userHasVerifiedShortCode()
}
} }
IncomingSasVerificationTransaction.UxState.VERIFIED -> { IncomingSasVerificationTransaction.UxState.VERIFIED -> {
bobSASLatch.countDown() bobSASLatch.countDown()
@@ -515,4 +534,96 @@ class SASTest : InstrumentedTest {
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(mTestHelper)
} }
@Test
fun test_ConcurrentStart() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val req = aliceVerificationService.requestKeyVerificationInDMs(
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
bobSession.myUserId,
cryptoTestData.roomId
)
var requestID : String? = null
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
requestID = prAlicePOV?.transactionId
Log.v("TEST", "== alicePOV is $prAlicePOV")
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
}
}
Log.v("TEST", "== requestID is $requestID")
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
val prBobPOV = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId)?.firstOrNull()
Log.v("TEST", "== prBobPOV is $prBobPOV")
prBobPOV?.transactionId == requestID
}
}
bobVerificationService.readyPendingVerification(
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
aliceSession.myUserId,
requestID!!
)
// wait for alice to get the ready
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
}
}
// Start concurrent!
aliceVerificationService.beginKeyVerificationInDMs(
VerificationMethod.SAS,
requestID!!,
cryptoTestData.roomId,
bobSession.myUserId,
bobSession.sessionParams.deviceId!!,
null)
bobVerificationService.beginKeyVerificationInDMs(
VerificationMethod.SAS,
requestID!!,
cryptoTestData.roomId,
aliceSession.myUserId,
aliceSession.sessionParams.deviceId!!,
null)
// we should reach SHOW SAS on both
var alicePovTx: SasVerificationTransaction?
var bobPovTx: SasVerificationTransaction?
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction
Log.v("TEST", "== alicePovTx is $alicePovTx")
alicePovTx?.state == VerificationTxState.ShortCodeReady
}
}
// wait for alice to get the ready
mTestHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction
Log.v("TEST", "== bobPovTx is $bobPovTx")
bobPovTx?.state == VerificationTxState.ShortCodeReady
}
}
cryptoTestData.cleanUp(mTestHelper)
}
} }

View File

@@ -0,0 +1,183 @@
/*
* 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 im.vector.matrix.android.session.room.timeline
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.checkSendOrder
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import timber.log.Timber
import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class TimelineBackToPreviousLastForwardTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
/**
* This test ensure that if we have a chunk in the timeline which is due to a sync, and we click to permalink of an
* even contained in a previous lastForward chunk, we will be able to go back to the live
*/
@Test
fun backToPreviousLastForwardTest() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession!!
val aliceRoomId = cryptoTestData.roomId
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
bobSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
bobTimeline.start()
var roomCreationEventId: String? = null
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
roomCreationEventId = snapshot.lastOrNull()?.root?.eventId
// Ok, we have the 8 first messages of the initial sync (room creation and bob join event)
snapshot.size == 8
}
bobTimeline.addListener(eventsListener)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
}
// Bob stop to sync
bobSession.stopSync()
val messageRoot = "First messages from Alice"
// Alice sends 30 messages
commonTestHelper.sendTextMessage(
roomFromAlicePOV,
messageRoot,
30)
// Bob start to sync
bobSession.startSync(true)
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// Ok, we have the 10 last messages from Alice.
snapshot.size == 10
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(messageRoot).orFalse() }
}
bobTimeline.addListener(eventsListener)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
}
// Bob navigate to the first event (room creation event), so inside the previous last forward chunk
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// The event is in db, so it is fetch and auto pagination occurs, half of the number of events we have for this chunk (?)
snapshot.size == 4
}
bobTimeline.addListener(eventsListener)
// Restart the timeline to the first sent event, which is already in the database, so pagination should start automatically
assertTrue(roomFromBobPOV.getTimeLineEvent(roomCreationEventId!!) != null)
bobTimeline.restartWithEventId(roomCreationEventId)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
}
// Bob scroll to the future
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// Bob can see the first event of the room (so Back pagination has worked)
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
// 8 for room creation item, and 30 for the forward pagination
&& snapshot.size == 38
&& snapshot.checkSendOrder(messageRoot, 30, 0)
}
bobTimeline.addListener(eventsListener)
bobTimeline.paginate(Timeline.Direction.FORWARDS, 50)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
}
bobTimeline.dispose()
cryptoTestData.cleanUp(commonTestHelper)
}
}

View File

@@ -0,0 +1,190 @@
/*
* 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 im.vector.matrix.android.session.room.timeline
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.checkSendOrder
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import timber.log.Timber
import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class TimelineForwardPaginationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
/**
* This test ensure that if we click to permalink, we will be able to go back to the live
*/
@Test
fun forwardPaginationTest() {
val numberOfMessagesToSend = 90
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
// Alice sends X messages
val message = "Message from Alice"
val sentMessages = commonTestHelper.sendTextMessage(
roomFromAlicePOV,
message,
numberOfMessagesToSend)
// Alice clear the cache
commonTestHelper.doSync<Unit> {
aliceSession.clearCache(it)
}
// And restarts the sync
aliceSession.startSync(true)
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
aliceTimeline.start()
// Alice sees the 10 last message of the room, and can only navigate BACKWARD
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root.content}")
}
// Ok, we have the 10 last messages of the initial sync
snapshot.size == 10
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(message).orFalse() }
}
// Open the timeline at last sent message
aliceTimeline.addListener(eventsListener)
commonTestHelper.await(lock)
aliceTimeline.removeAllListeners()
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
}
// Alice navigates to the first message of the room, which is not in its database. A GET /context is performed
// Then she can paginate BACKWARD and FORWARD
run {
val lock = CountDownLatch(1)
val aliceEventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root.content}")
}
// The event is not in db, so it is fetch alone
snapshot.size == 1
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith("Message from Alice").orFalse() }
}
aliceTimeline.addListener(aliceEventsListener)
// Restart the timeline to the first sent event
aliceTimeline.restartWithEventId(sentMessages.last().eventId)
commonTestHelper.await(lock)
aliceTimeline.removeAllListeners()
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
}
// Alice paginates BACKWARD and FORWARD of 50 events each
// Then she can only navigate FORWARD
run {
val lock = CountDownLatch(1)
val aliceEventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root.content}")
}
// Alice can see the first event of the room (so Back pagination has worked)
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
// 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination
&& snapshot.size == 6 + 1 + 50
}
aliceTimeline.addListener(aliceEventsListener)
// Restart the timeline to the first sent event
// We ask to load event backward and forward
aliceTimeline.paginate(Timeline.Direction.BACKWARDS, 50)
aliceTimeline.paginate(Timeline.Direction.FORWARDS, 50)
commonTestHelper.await(lock)
aliceTimeline.removeAllListeners()
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
}
// Alice paginates once again FORWARD for 50 events
// All the timeline is retrieved, she cannot paginate anymore in both direction
run {
val lock = CountDownLatch(1)
val aliceEventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Alice timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root.content}")
}
// 6 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room)
snapshot.size == 6 + numberOfMessagesToSend
&& snapshot.checkSendOrder(message, numberOfMessagesToSend, 0)
}
aliceTimeline.addListener(aliceEventsListener)
// Ask for a forward pagination
aliceTimeline.paginate(Timeline.Direction.FORWARDS, 50)
commonTestHelper.await(lock)
aliceTimeline.removeAllListeners()
// The timeline is fully loaded
aliceTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
}
aliceTimeline.dispose()
cryptoTestData.cleanUp(commonTestHelper)
}
}

View File

@@ -0,0 +1,241 @@
/*
* 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 im.vector.matrix.android.session.room.timeline
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.checkSendOrder
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import timber.log.Timber
import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class TimelinePreviousLastForwardTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
/**
* This test ensure that if we have a chunk in the timeline which is due to a sync, and we click to permalink, we will be able to go back to the live
*/
@Test
fun previousLastForwardTest() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession!!
val aliceRoomId = cryptoTestData.roomId
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
bobSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(30))
bobTimeline.start()
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// Ok, we have the 8 first messages of the initial sync (room creation and bob invite and join events)
snapshot.size == 8
}
bobTimeline.addListener(eventsListener)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
}
// Bob stop to sync
bobSession.stopSync()
val firstMessage = "First messages from Alice"
// Alice sends 30 messages
val firstMessageFromAliceId = commonTestHelper.sendTextMessage(
roomFromAlicePOV,
firstMessage,
30)
.last()
.eventId
// Bob start to sync
bobSession.startSync(true)
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
snapshot.size == 10
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(firstMessage).orFalse() }
}
bobTimeline.addListener(eventsListener)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
}
// Bob stop to sync
bobSession.stopSync()
val secondMessage = "Second messages from Alice"
// Alice sends again 30 messages
commonTestHelper.sendTextMessage(
roomFromAlicePOV,
secondMessage,
30)
// Bob start to sync
bobSession.startSync(true)
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk
snapshot.size == 10
&& snapshot.all { it.root.content.toModel<MessageContent>()?.body?.startsWith(secondMessage).orFalse() }
}
bobTimeline.addListener(eventsListener)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
}
// Bob navigate to the first message sent from Alice
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// The event is not in db, so it is fetch
snapshot.size == 1
}
bobTimeline.addListener(eventsListener)
// Restart the timeline to the first sent event, and paginate in both direction
bobTimeline.restartWithEventId(firstMessageFromAliceId)
bobTimeline.paginate(Timeline.Direction.BACKWARDS, 50)
bobTimeline.paginate(Timeline.Direction.FORWARDS, 50)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeTrue()
}
// Paginate in both direction
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
snapshot.size == 8 + 1 + 35
}
bobTimeline.addListener(eventsListener)
// Paginate in both direction
bobTimeline.paginate(Timeline.Direction.BACKWARDS, 50)
// Ensure the chunk in the middle is included in the next pagination
bobTimeline.paginate(Timeline.Direction.FORWARDS, 35)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeTrue()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
}
// Bob scroll to the future, till the live
run {
val lock = CountDownLatch(1)
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
Timber.e("Bob timeline updated: with ${snapshot.size} events:")
snapshot.forEach {
Timber.w(" event ${it.root}")
}
// Bob can see the first event of the room (so Back pagination has worked)
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE
// 8 for room creation item 60 message from Alice
&& snapshot.size == 8 + 60
&& snapshot.checkSendOrder(secondMessage, 30, 0)
&& snapshot.checkSendOrder(firstMessage, 30, 30)
}
bobTimeline.addListener(eventsListener)
bobTimeline.paginate(Timeline.Direction.FORWARDS, 50)
commonTestHelper.await(lock)
bobTimeline.removeAllListeners()
bobTimeline.hasMoreToLoad(Timeline.Direction.FORWARDS).shouldBeFalse()
bobTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
}
bobTimeline.dispose()
cryptoTestData.cleanUp(commonTestHelper)
}
}

View File

@@ -20,7 +20,6 @@ package im.vector.matrix.android.internal.network.interceptors
import im.vector.matrix.android.internal.di.MatrixScope import im.vector.matrix.android.internal.di.MatrixScope
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer import okio.Buffer
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
@@ -37,7 +36,7 @@ import javax.inject.Inject
* non-production environment. * non-production environment.
*/ */
@MatrixScope @MatrixScope
internal class CurlLoggingInterceptor @Inject constructor(private val logger: HttpLoggingInterceptor.Logger) internal class CurlLoggingInterceptor @Inject constructor()
: Interceptor { : Interceptor {
/** /**
@@ -97,8 +96,8 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
// Add Json formatting // Add Json formatting
curlCmd += " | python -m json.tool" curlCmd += " | python -m json.tool"
logger.log("--- cURL (" + request.url + ")") Timber.d("--- cURL (${request.url})")
logger.log(curlCmd) Timber.d(curlCmd)
return chain.proceed(request) return chain.proceed(request)
} }

View File

@@ -23,7 +23,6 @@ import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.auth.AuthenticationService import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.crypto.MXCryptoConfig
import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
@@ -32,19 +31,10 @@ import im.vector.matrix.android.internal.network.UserAgentHolder
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import java.io.InputStream import java.io.InputStream
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject import javax.inject.Inject
data class MatrixConfiguration(
val applicationFlavor: String = "Default-application-flavor",
val cryptoConfig: MXCryptoConfig = MXCryptoConfig()
) {
interface Provider {
fun providesMatrixConfiguration(): MatrixConfiguration
}
}
/** /**
* This is the main entry point to the matrix sdk. * This is the main entry point to the matrix sdk.
* To get the singleton instance, use getInstance static method. * To get the singleton instance, use getInstance static method.
@@ -61,7 +51,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
Monarchy.init(context) Monarchy.init(context)
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this) DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) { if (context.applicationContext !is Configuration.Provider) {
WorkManager.initialize(context, Configuration.Builder().build()) WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
} }
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
} }

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 im.vector.matrix.android.api
import im.vector.matrix.android.api.crypto.MXCryptoConfig
import java.net.Proxy
data class MatrixConfiguration(
val applicationFlavor: String = "Default-application-flavor",
val cryptoConfig: MXCryptoConfig = MXCryptoConfig(),
/**
* Optional proxy to connect to the matrix servers
* You can create one using for instance Proxy(proxyType, InetSocketAddress(hostname, port)
*/
val proxy: Proxy? = null
) {
/**
* Can be implemented by your Application class
*/
interface Provider {
fun providesMatrixConfiguration(): MatrixConfiguration
}
}

View File

@@ -20,9 +20,9 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.data.LoginFlowResult import im.vector.matrix.android.api.auth.data.LoginFlowResult
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.auth.login.LoginWizard import im.vector.matrix.android.api.auth.login.LoginWizard
import im.vector.matrix.android.api.auth.registration.RegistrationWizard import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.api.auth.wellknown.WellknownResult
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
@@ -30,13 +30,17 @@ import im.vector.matrix.android.api.util.Cancelable
* This interface defines methods to authenticate or to create an account to a matrix server. * This interface defines methods to authenticate or to create an account to a matrix server.
*/ */
interface AuthenticationService { interface AuthenticationService {
/** /**
* Request the supported login flows for this homeserver. * Request the supported login flows for this homeserver.
* This is the first method to call to be able to get a wizard to login or the create an account * This is the first method to call to be able to get a wizard to login or the create an account
*/ */
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable
/**
* Request the supported login flows for the corresponding sessionId.
*/
fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
/** /**
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first. * Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
*/ */
@@ -74,19 +78,26 @@ interface AuthenticationService {
*/ */
fun getLastAuthenticatedSession(): Session? fun getLastAuthenticatedSession(): Session?
/**
* Get an authenticated session. You should at least call authenticate one time before.
* If you logout, this session will no longer be valid.
*
* @param sessionParams the sessionParams to open with.
* @return the associated session if any, or null
*/
fun getSession(sessionParams: SessionParams): Session?
/** /**
* Create a session after a SSO successful login * Create a session after a SSO successful login
*/ */
fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
credentials: Credentials, credentials: Credentials,
callback: MatrixCallback<Session>): Cancelable callback: MatrixCallback<Session>): Cancelable
/**
* Perform a wellknown request, using the domain from the matrixId
*/
fun getWellKnownData(matrixId: String,
callback: MatrixCallback<WellknownResult>): Cancelable
/**
* Authenticate with a matrixId and a password
* Usually call this after a successful call to getWellKnownData()
*/
fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig,
matrixId: String,
password: String,
initialDeviceName: String,
callback: MatrixCallback<Session>): Cancelable
} }

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 im.vector.matrix.android.api.auth
/**
* Path to use when the client does not supported any or all login flows
* Ref: https://matrix.org/docs/spec/client_server/latest#login-fallback
* */
const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
/**
* Path to use when the client does not supported any or all registration flows
* Not documented
*/
const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
/**
* Path to use when the client want to connect using SSO
* Ref: https://matrix.org/docs/spec/client_server/latest#sso-client-login
*/
const val SSO_FALLBACK_PATH = "/_matrix/client/r0/login/sso/redirect"
const val SSO_REDIRECT_URL_PARAM = "redirectUrl"

View File

@@ -24,16 +24,38 @@ import im.vector.matrix.android.internal.util.md5
* This data class hold credentials user data. * This data class hold credentials user data.
* You shouldn't have to instantiate it. * You shouldn't have to instantiate it.
* The access token should be use to authenticate user in all server requests. * The access token should be use to authenticate user in all server requests.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-login
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class Credentials( data class Credentials(
/**
* The fully-qualified Matrix ID that has been registered.
*/
@Json(name = "user_id") val userId: String, @Json(name = "user_id") val userId: String,
@Json(name = "home_server") val homeServer: String, /**
* An access token for the account. This access token can then be used to authorize other requests.
*/
@Json(name = "access_token") val accessToken: String, @Json(name = "access_token") val accessToken: String,
/**
* Not documented
*/
@Json(name = "refresh_token") val refreshToken: String?, @Json(name = "refresh_token") val refreshToken: String?,
/**
* The server_name of the homeserver on which the account has been registered.
* @Deprecated. Clients should extract the server_name from user_id (by splitting at the first colon)
* if they require it. Note also that homeserver is not spelt this way.
*/
@Json(name = "home_server") val homeServer: String,
/**
* ID of the logged-in device. Will be the same as the corresponding parameter in the request, if one was specified.
*/
@Json(name = "device_id") val deviceId: String?, @Json(name = "device_id") val deviceId: String?,
// Optional data that may contain info to override home server and/or identity server /**
@Json(name = "well_known") val wellKnown: WellKnown? = null * Optional client configuration provided by the server. If present, clients SHOULD use the provided object to
* reconfigure themselves, optionally validating the URLs within.
* This object takes the same form as the one returned from .well-known autodiscovery.
*/
@Json(name = "well_known") val discoveryInformation: DiscoveryInformation? = null
) )
internal fun Credentials.sessionId(): String { internal fun Credentials.sessionId(): String {

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 im.vector.matrix.android.api.auth.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* This is a light version of Wellknown model, used for login response
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-login
*/
@JsonClass(generateAdapter = true)
data class DiscoveryInformation(
/**
* Required. Used by clients to discover homeserver information.
*/
@Json(name = "m.homeserver")
val homeServer: WellKnownBaseConfig? = null,
/**
* Used by clients to discover identity server information.
* Note: matrix.org does not send this field
*/
@Json(name = "m.identity_server")
val identityServer: WellKnownBaseConfig? = null
)

View File

@@ -21,7 +21,48 @@ package im.vector.matrix.android.api.auth.data
* You don't have to manually instantiate it. * You don't have to manually instantiate it.
*/ */
data class SessionParams( data class SessionParams(
/**
* Please consider using shortcuts instead
*/
val credentials: Credentials, val credentials: Credentials,
/**
* Please consider using shortcuts instead
*/
val homeServerConnectionConfig: HomeServerConnectionConfig, val homeServerConnectionConfig: HomeServerConnectionConfig,
/**
* Set to false if the current token is not valid anymore. Application should not have to use this info.
*/
val isTokenValid: Boolean val isTokenValid: Boolean
) ) {
/*
* Shortcuts. Usually the application should only need to use these shortcuts
*/
/**
* The userId of the session (Ex: "@user:domain.org")
*/
val userId = credentials.userId
/**
* The deviceId of the session (Ex: "ABCDEFGH")
*/
val deviceId = credentials.deviceId
/**
* The current homeserver Url. It can be different that the homeserver url entered
* during login phase, because a redirection may have occurred
*/
val homeServerUrl = homeServerConnectionConfig.homeServerUri.toString()
/**
* The current homeserver host
*/
val homeServerHost = homeServerConnectionConfig.homeServerUri.host
/**
* The default identity server url if any, returned by the homeserver during login phase
*/
val defaultIdentityServerUrl = homeServerConnectionConfig.identityServerUri?.toString()
}

View File

@@ -18,6 +18,7 @@ package im.vector.matrix.android.api.auth.data
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.JsonDict
/** /**
* https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery * https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
@@ -52,7 +53,7 @@ data class WellKnown(
val identityServer: WellKnownBaseConfig? = null, val identityServer: WellKnownBaseConfig? = null,
@Json(name = "m.integrations") @Json(name = "m.integrations")
val integrations: Map<String, @JvmSuppressWildcards Any>? = null val integrations: JsonDict? = null
) { ) {
/** /**
* Returns the list of integration managers proposed * Returns the list of integration managers proposed

View File

@@ -16,6 +16,6 @@
package im.vector.matrix.android.api.auth.data package im.vector.matrix.android.api.auth.data
data class WellKnownManagerConfig( data class WellKnownManagerConfig(
val apiUrl : String, val apiUrl: String,
val uiUrl: String val uiUrl: String
) )

View File

@@ -0,0 +1,55 @@
/*
* 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 im.vector.matrix.android.api.auth.wellknown
import im.vector.matrix.android.api.auth.data.WellKnown
/**
* Ref: https://matrix.org/docs/spec/client_server/latest#well-known-uri
*/
sealed class WellknownResult {
/**
* The provided matrixId is no valid. Unable to extract a domain name.
*/
object InvalidMatrixId : WellknownResult()
/**
* Retrieve the specific piece of information from the user in a way which fits within the existing client user experience,
* if the client is inclined to do so. Failure can take place instead if no good user experience for this is possible at this point.
*/
data class Prompt(val homeServerUrl: String,
val identityServerUrl: String?,
val wellKnown: WellKnown) : WellknownResult()
/**
* Stop the current auto-discovery mechanism. If no more auto-discovery mechanisms are available,
* then the client may use other methods of determining the required parameters, such as prompting the user, or using default values.
*/
object Ignore : WellknownResult()
/**
* Inform the user that auto-discovery failed due to invalid/empty data and PROMPT for the parameter.
*/
object FailPrompt : WellknownResult()
/**
* Inform the user that auto-discovery did not return any usable URLs. Do not continue further with the current login process.
* At this point, valid data was obtained, but no homeserver is available to serve the client.
* No further guess should be attempted and the user should make a conscientious decision what to do next.
*/
object FailError : WellknownResult()
}

View File

@@ -19,9 +19,17 @@ package im.vector.matrix.android.api.crypto
/** /**
* Class to define the parameters used to customize or configure the end-to-end crypto. * Class to define the parameters used to customize or configure the end-to-end crypto.
*/ */
data class MXCryptoConfig( data class MXCryptoConfig constructor(
// Tell whether the encryption of the event content is enabled for the invited members. // Tell whether the encryption of the event content is enabled for the invited members.
// SDK clients can disable this by settings it to false. // SDK clients can disable this by settings it to false.
// Note that the encryption for the invited members will be blocked if the history visibility is "joined". // Note that the encryption for the invited members will be blocked if the history visibility is "joined".
var enableEncryptionForInvitedMembers: Boolean = true val enableEncryptionForInvitedMembers: Boolean = true,
/**
* If set to true, the SDK will automatically ignore room key request (gossiping)
* coming from your other untrusted sessions (or blocked).
* If set to false, the request will be forwarded to the application layer; in this
* case the application can decide to prompt the user.
*/
val discardRoomKeyRequestsFromUntrustedDevices: Boolean = true
) )

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 im.vector.matrix.android.api.extensions
inline fun <A> tryThis(operation: () -> A): A? {
return try {
operation()
} catch (any: Throwable) {
null
}
}

View File

@@ -16,6 +16,10 @@
package im.vector.matrix.android.api.failure package im.vector.matrix.android.api.failure
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.di.MoshiProvider
import java.io.IOException
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
fun Throwable.is401() = fun Throwable.is401() =
@@ -29,5 +33,27 @@ fun Throwable.isTokenError() =
fun Throwable.shouldBeRetried(): Boolean { fun Throwable.shouldBeRetried(): Boolean {
return this is Failure.NetworkConnection return this is Failure.NetworkConnection
|| this is IOException
|| (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED) || (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED)
} }
fun Throwable.isInvalidPassword(): Boolean {
return this is Failure.ServerError
&& error.code == MatrixError.M_FORBIDDEN
&& error.message == "Invalid password"
}
/**
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
*/
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
return if (this is Failure.OtherServerError && this.httpCode == 401) {
tryThis {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(this.errorBody)
}
} else {
null
}
}

View File

@@ -39,7 +39,10 @@ data class MatrixError(
// For M_LIMIT_EXCEEDED // For M_LIMIT_EXCEEDED
@Json(name = "retry_after_ms") val retryAfterMillis: Long? = null, @Json(name = "retry_after_ms") val retryAfterMillis: Long? = null,
// For M_UNKNOWN_TOKEN // For M_UNKNOWN_TOKEN
@Json(name = "soft_logout") val isSoftLogout: Boolean = false @Json(name = "soft_logout") val isSoftLogout: Boolean = false,
// For M_INVALID_PEPPER
// {"error": "pepper does not match 'erZvr'", "lookup_pepper": "pQgMS", "algorithm": "sha256", "errcode": "M_INVALID_PEPPER"}
@Json(name = "lookup_pepper") val newLookupPepper: String? = null
) { ) {
companion object { companion object {
@@ -129,6 +132,11 @@ data class MatrixError(
/** (Not documented yet) */ /** (Not documented yet) */
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION" const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
const val M_TERMS_NOT_SIGNED = "M_TERMS_NOT_SIGNED"
// For identity service
const val M_INVALID_PEPPER = "M_INVALID_PEPPER"
// Possible value for "limit_type" // Possible value for "limit_type"
const val LIMIT_TYPE_MAU = "monthly_active_user" const val LIMIT_TYPE_MAU = "monthly_active_user"
} }

View File

@@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.failure.GlobalError import im.vector.matrix.android.api.failure.GlobalError
import im.vector.matrix.android.api.pushrules.PushRuleService import im.vector.matrix.android.api.pushrules.PushRuleService
import im.vector.matrix.android.api.session.account.AccountService
import im.vector.matrix.android.api.session.accountdata.AccountDataService import im.vector.matrix.android.api.session.accountdata.AccountDataService
import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
@@ -29,6 +30,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.identity.IdentityService
import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.profile.ProfileService
import im.vector.matrix.android.api.session.pushers.PushersService import im.vector.matrix.android.api.session.pushers.PushersService
import im.vector.matrix.android.api.session.room.RoomDirectoryService import im.vector.matrix.android.api.session.room.RoomDirectoryService
@@ -38,6 +40,7 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer
import im.vector.matrix.android.api.session.signout.SignOutService import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.terms.TermsService
import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.UserService
/** /**
@@ -53,13 +56,15 @@ interface Session :
SignOutService, SignOutService,
FilterService, FilterService,
FileService, FileService,
TermsService,
ProfileService, ProfileService,
PushRuleService, PushRuleService,
PushersService, PushersService,
InitialSyncProgressService, InitialSyncProgressService,
HomeServerCapabilitiesService, HomeServerCapabilitiesService,
SecureStorageService, SecureStorageService,
AccountDataService { AccountDataService,
AccountService {
/** /**
* The params associated to the session * The params associated to the session
@@ -75,7 +80,7 @@ interface Session :
* Useful shortcut to get access to the userId * Useful shortcut to get access to the userId
*/ */
val myUserId: String val myUserId: String
get() = sessionParams.credentials.userId get() = sessionParams.userId
/** /**
* The sessionId * The sessionId
@@ -143,6 +148,11 @@ interface Session :
*/ */
fun cryptoService(): CryptoService fun cryptoService(): CryptoService
/**
* Returns the identity service associated with the session
*/
fun identityService(): IdentityService
/** /**
* Add a listener to the session. * Add a listener to the session.
* @param listener the listener to add. * @param listener the listener to add.

View File

@@ -0,0 +1,50 @@
/*
* 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 im.vector.matrix.android.api.session.account
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
/**
* This interface defines methods to manage the account. It's implemented at the session level.
*/
interface AccountService {
/**
* Ask the homeserver to change the password.
* @param password Current password.
* @param newPassword New password
*/
fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
/**
* Deactivate the account.
*
* This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register
* the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account
* details from your identity server. <b>This action is irreversible</b>.\n\nDeactivating your account <b>does not by default
* cause us to forget messages you have sent</b>. If you would like us to forget your messages, please tick the box below.
*
* Message visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not
* be shared with any new or unregistered users, but registered users who already have access to these messages will still
* have access to their copy.
*
* @param password the account password
* @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see
* an incomplete view of conversations
*/
fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable
}

View File

@@ -16,6 +16,7 @@
package im.vector.matrix.android.api.session.content package im.vector.matrix.android.api.session.content
import android.net.Uri
import android.os.Parcelable import android.os.Parcelable
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@@ -29,8 +30,7 @@ data class ContentAttachmentData(
val width: Long? = 0, val width: Long? = 0,
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED, val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
val name: String? = null, val name: String? = null,
val queryUri: String, val queryUri: Uri,
val path: String,
private val mimeType: String?, private val mimeType: String?,
val type: Type val type: Type
) : Parcelable { ) : Parcelable {

View File

@@ -26,6 +26,11 @@ interface ContentUrlResolver {
SCALE("scale") SCALE("scale")
} }
/**
* URL to use to upload content
*/
val uploadUrl: String
/** /**
* Get the actual URL for accessing the full-size image of a Matrix media content URI. * Get the actual URL for accessing the full-size image of a Matrix media content URI.
* *

View File

@@ -22,12 +22,14 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.listeners.ProgressListener import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener
import im.vector.matrix.android.api.session.crypto.verification.VerificationService import im.vector.matrix.android.api.session.crypto.verification.VerificationService
import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.NewSessionListener
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
@@ -86,15 +88,19 @@ interface CryptoService {
fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
fun requestRoomKeyForEvent(event: Event)
fun reRequestRoomKeyForEvent(event: Event) fun reRequestRoomKeyForEvent(event: Event)
fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody)
fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) fun addRoomKeysRequestListener(listener: GossipingRequestListener)
fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) fun removeRoomKeysRequestListener(listener: GossipingRequestListener)
fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) fun fetchDevicesList(callback: MatrixCallback<DevicesListResponse>)
fun getMyDevicesInfo() : List<DeviceInfo>
fun getLiveMyDevicesInfo() : LiveData<List<DeviceInfo>>
fun getDeviceInfo(deviceId: String, callback: MatrixCallback<DeviceInfo>) fun getDeviceInfo(deviceId: String, callback: MatrixCallback<DeviceInfo>)
@@ -107,6 +113,8 @@ interface CryptoService {
roomId: String, roomId: String,
callback: MatrixCallback<MXEncryptEventContentResult>) callback: MatrixCallback<MXEncryptEventContentResult>)
fun discardOutboundSession(roomId: String)
@Throws(MXCryptoError::class) @Throws(MXCryptoError::class)
fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult
@@ -129,4 +137,8 @@ interface CryptoService {
fun addNewSessionListener(newSessionListener: NewSessionListener) fun addNewSessionListener(newSessionListener: NewSessionListener)
fun removeSessionListener(listener: NewSessionListener) fun removeSessionListener(listener: NewSessionListener)
fun getOutgoingRoomKeyRequest(): List<OutgoingRoomKeyRequest>
fun getIncomingRoomKeyRequest(): List<IncomingRoomKeyRequest>
fun getGossipingEventsTrail(): List<Event>
} }

View File

@@ -22,6 +22,7 @@ import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustResult import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustResult
import im.vector.matrix.android.internal.crypto.crosssigning.UserTrustResult import im.vector.matrix.android.internal.crypto.crosssigning.UserTrustResult
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
interface CrossSigningService { interface CrossSigningService {
@@ -52,6 +53,10 @@ interface CrossSigningService {
fun getMyCrossSigningKeys(): MXCrossSigningInfo? fun getMyCrossSigningKeys(): MXCrossSigningInfo?
fun getCrossSigningPrivateKeys(): PrivateKeysInfo?
fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>>
fun canCrossSign(): Boolean fun canCrossSign(): Boolean
fun trustUser(otherUserId: String, fun trustUser(otherUserId: String,
@@ -68,4 +73,7 @@ interface CrossSigningService {
fun checkDeviceTrust(otherUserId: String, fun checkDeviceTrust(otherUserId: String,
otherDeviceId: String, otherDeviceId: String,
locallyTrusted: Boolean?): DeviceTrustResult locallyTrusted: Boolean?): DeviceTrustResult
fun onSecretSSKGossip(sskPrivateKey: String)
fun onSecretUSKGossip(uskPrivateKey: String)
} }

View File

@@ -21,3 +21,5 @@ const val MASTER_KEY_SSSS_NAME = "m.cross_signing.master"
const val USER_SIGNING_KEY_SSSS_NAME = "m.cross_signing.user_signing" const val USER_SIGNING_KEY_SSSS_NAME = "m.cross_signing.user_signing"
const val SELF_SIGNING_KEY_SSSS_NAME = "m.cross_signing.self_signing" const val SELF_SIGNING_KEY_SSSS_NAME = "m.cross_signing.self_signing"
const val KEYBACKUP_SECRET_SSSS_NAME = "m.megolm_backup.v1"

View File

@@ -24,6 +24,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCre
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo
interface KeysBackupService { interface KeysBackupService {
/** /**
@@ -172,6 +173,8 @@ interface KeysBackupService {
password: String, password: String,
callback: MatrixCallback<Unit>) callback: MatrixCallback<Unit>)
fun onSecretKeyGossip(secret: String)
/** /**
* Restore a backup with a recovery key from a given backup version stored on the homeserver. * Restore a backup with a recovery key from a given backup version stored on the homeserver.
* *
@@ -210,4 +213,10 @@ interface KeysBackupService {
val isEnabled: Boolean val isEnabled: Boolean
val isStucked: Boolean val isStucked: Boolean
val state: KeysBackupState val state: KeysBackupState
// For gossiping
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
fun isValidRecoveryKeyForCurrentVersion(recoveryKey: String, callback: MatrixCallback<Boolean>)
} }

View File

@@ -17,12 +17,13 @@
package im.vector.matrix.android.api.session.crypto.keyshare package im.vector.matrix.android.api.session.crypto.keyshare
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCancellation import im.vector.matrix.android.internal.crypto.IncomingRequestCancellation
import im.vector.matrix.android.internal.crypto.IncomingSecretShareRequest
/** /**
* Room keys events listener * Room keys events listener
*/ */
interface RoomKeysRequestListener { interface GossipingRequestListener {
/** /**
* An room key request has been received. * An room key request has been received.
* *
@@ -30,10 +31,16 @@ interface RoomKeysRequestListener {
*/ */
fun onRoomKeyRequest(request: IncomingRoomKeyRequest) fun onRoomKeyRequest(request: IncomingRoomKeyRequest)
/**
* Returns the secret value to be shared
* @return true if is handled
*/
fun onSecretShareRequest(request: IncomingSecretShareRequest) : Boolean
/** /**
* A room key request cancellation has been received. * A room key request cancellation has been received.
* *
* @param request the cancellation request * @param request the cancellation request
*/ */
fun onRoomKeyRequestCancellation(request: IncomingRoomKeyRequestCancellation) fun onRoomKeyRequestCancellation(request: IncomingRequestCancellation)
} }

View File

@@ -16,7 +16,10 @@
package im.vector.matrix.android.api.session.crypto.verification package im.vector.matrix.android.api.session.crypto.verification
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
data class EmojiRepresentation(val emoji: String, data class EmojiRepresentation(val emoji: String,
@StringRes val nameResId: Int) @StringRes val nameResId: Int,
@DrawableRes val drawableRes: Int? = null
)

View File

@@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.crypto.verification package im.vector.matrix.android.api.session.crypto.verification
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.LocalEcho import im.vector.matrix.android.api.session.events.model.LocalEcho
/** /**
@@ -59,6 +60,8 @@ interface VerificationService {
roomId: String, roomId: String,
localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest
fun cancelVerificationRequest(request: PendingVerificationRequest)
/** /**
* Request a key verification from another user using toDevice events. * Request a key verification from another user using toDevice events.
*/ */
@@ -136,4 +139,6 @@ interface VerificationService {
return age in tooInThePast..tooInTheFuture return age in tooInThePast..tooInTheFuture
} }
} }
fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event)
} }

View File

@@ -43,6 +43,7 @@ sealed class VerificationTxState {
// Will be used to ask the user if the other user has correctly scanned // Will be used to ask the user if the other user has correctly scanned
object QrScannedByOther : VerificationQrTxState() object QrScannedByOther : VerificationQrTxState()
object WaitingOtherReciprocateConfirm : VerificationQrTxState()
// Terminal states // Terminal states
abstract class TerminalTxState : VerificationTxState() abstract class TerminalTxState : VerificationTxState()

View File

@@ -142,12 +142,12 @@ data class Event(
} }
fun toContentStringWithIndent(): String { fun toContentStringWithIndent(): String {
val contentMap = toContent().toMutableMap() val contentMap = toContent()
return JSONObject(contentMap).toString(4) return JSONObject(contentMap).toString(4)
} }
fun toClearContentStringWithIndent(): String? { fun toClearContentStringWithIndent(): String? {
val contentMap = this.mxDecryptionResult?.payload?.toMutableMap() val contentMap = this.mxDecryptionResult?.payload
val adapter = MoshiProvider.providesMoshi().adapter(Map::class.java) val adapter = MoshiProvider.providesMoshi().adapter(Map::class.java)
return contentMap?.let { JSONObject(adapter.toJson(it)).toString(4) } return contentMap?.let { JSONObject(adapter.toJson(it)).toString(4) }
} }
@@ -220,3 +220,11 @@ fun Event.isImageMessage(): Boolean {
else -> false else -> false
} }
} }
fun Event.isVideoMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
MessageType.MSGTYPE_VIDEO -> true
else -> false
}
}

View File

@@ -81,6 +81,9 @@ object EventType {
// Relation Events // Relation Events
const val REACTION = "m.reaction" const val REACTION = "m.reaction"
// Unwedging
internal const val DUMMY = "m.dummy"
private val STATE_EVENTS = listOf( private val STATE_EVENTS = listOf(
STATE_ROOM_NAME, STATE_ROOM_NAME,
STATE_ROOM_TOPIC, STATE_ROOM_TOPIC,

View File

@@ -17,10 +17,22 @@
package im.vector.matrix.android.api.session.homeserver package im.vector.matrix.android.api.session.homeserver
data class HomeServerCapabilities( data class HomeServerCapabilities(
/**
* True if it is possible to change the password of the account.
*/
val canChangePassword: Boolean = true,
/** /**
* Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet * Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet
*/ */
val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN,
/**
* Last version identity server and binding supported
*/
val lastVersionIdentityServerSupported: Boolean = false,
/**
* Default identity server url, provided in Wellknown
*/
val defaultIdentityServerUrl: String? = null
) { ) {
companion object { companion object {
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L

View File

@@ -0,0 +1,22 @@
/*
* 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 im.vector.matrix.android.api.session.identity
data class FoundThreePid(
val threePid: ThreePid,
val matrixId: String
)

View File

@@ -0,0 +1,109 @@
/*
* 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 im.vector.matrix.android.api.session.identity
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
/**
* Provides access to the identity server configuration and services identity server can provide
*/
interface IdentityService {
/**
* Return the default identity server of the user, which may have been provided at login time by the homeserver,
* or by the Well-known setup of the homeserver
* It may be different from the current configured identity server
*/
fun getDefaultIdentityServer(): String?
/**
* Return the current identity server URL used by this account. Returns null if no identity server is configured.
*/
fun getCurrentIdentityServerUrl(): String?
/**
* Check if the identity server is valid
* See https://matrix.org/docs/spec/identity_service/latest#status-check
* RiotX SDK only supports identity server API v2
*/
fun isValidIdentityServer(url: String, callback: MatrixCallback<Unit>): Cancelable
/**
* Update the identity server url.
* If successful, any previous identity server will be disconnected.
* In case of error, any previous identity server will remain configured.
* @param url the new url.
* @param callback will notify the user if change is successful. The String will be the final url of the identity server.
* The SDK can prepend "https://" for instance.
*/
fun setNewIdentityServer(url: String, callback: MatrixCallback<String>): Cancelable
/**
* Disconnect (logout) from the current identity server
*/
fun disconnect(callback: MatrixCallback<Unit>): Cancelable
/**
* This will ask the identity server to send an email or an SMS to let the user confirm he owns the ThreePid
*/
fun startBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
/**
* This will cancel a pending binding of threePid.
*/
fun cancelBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
/**
* This will ask the identity server to send an new email or a new SMS to let the user confirm he owns the ThreePid
*/
fun sendAgainValidationCode(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
/**
* Submit the code that the identity server has sent to the user (in email or SMS)
* Once successful, you will have to call [finalizeBindThreePid]
* @param code the code sent to the user
*/
fun submitValidationToken(threePid: ThreePid, code: String, callback: MatrixCallback<Unit>): Cancelable
/**
* This will perform the actual association of ThreePid and Matrix account
*/
fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
/**
* Unbind a threePid
* The request will actually be done on the homeserver
*/
fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
/**
* Search MatrixId of users providing email and phone numbers
*/
fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable
/**
* Get the status of the current user's threePid
* A lookup will be performed, but also pending binding state will be restored
*
* @param threePids the list of threePid the user owns (retrieved form the homeserver)
* @param callback onSuccess will be called with a map of ThreePid -> SharedState
*/
fun getShareStatus(threePids: List<ThreePid>, callback: MatrixCallback<Map<ThreePid, SharedState>>): Cancelable
fun addListener(listener: IdentityServiceListener)
fun removeListener(listener: IdentityServiceListener)
}

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 im.vector.matrix.android.api.session.identity
sealed class IdentityServiceError : Throwable() {
object OutdatedIdentityServer : IdentityServiceError()
object OutdatedHomeServer : IdentityServiceError()
object NoIdentityServerConfigured : IdentityServiceError()
object TermsNotSignedException : IdentityServiceError()
object BulkLookupSha256NotSupported : IdentityServiceError()
object BindingError : IdentityServiceError()
object NoCurrentBindingError : IdentityServiceError()
}

View File

@@ -0,0 +1,21 @@
/*
* 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 im.vector.matrix.android.api.session.identity
interface IdentityServiceListener {
fun onIdentityServerChange()
}

View File

@@ -0,0 +1,23 @@
/*
* 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 im.vector.matrix.android.api.session.identity
enum class SharedState {
SHARED,
NOT_SHARED,
BINDING_IN_PROGRESS
}

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 im.vector.matrix.android.api.session.identity
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import im.vector.matrix.android.internal.session.profile.ThirdPartyIdentifier
sealed class ThreePid(open val value: String) {
data class Email(val email: String) : ThreePid(email)
data class Msisdn(val msisdn: String) : ThreePid(msisdn)
}
internal fun ThreePid.toMedium(): String {
return when (this) {
is ThreePid.Email -> ThirdPartyIdentifier.MEDIUM_EMAIL
is ThreePid.Msisdn -> ThirdPartyIdentifier.MEDIUM_MSISDN
}
}
@Throws(NumberParseException::class)
internal fun ThreePid.Msisdn.getCountryCode(): String {
return with(PhoneNumberUtil.getInstance()) {
getRegionCodeForCountryCode(parse("+$msisdn", null).countryCode)
}
}

View File

@@ -17,7 +17,9 @@
package im.vector.matrix.android.api.session.profile package im.vector.matrix.android.api.session.profile
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.identity.ThreePid
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.Optional
@@ -53,4 +55,15 @@ interface ProfileService {
* *
*/ */
fun getProfile(userId: String, matrixCallback: MatrixCallback<JsonDict>): Cancelable fun getProfile(userId: String, matrixCallback: MatrixCallback<JsonDict>): Cancelable
/**
* Get the current user 3Pids
*/
fun getThreePids(): List<ThreePid>
/**
* Get the current user 3Pids Live
* @param refreshData set to true to fetch data from the homeserver
*/
fun getThreePidsLive(refreshData: Boolean): LiveData<List<ThreePid>>
} }

View File

@@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService import im.vector.matrix.android.api.session.room.state.StateService
import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.session.room.typing.TypingService import im.vector.matrix.android.api.session.room.typing.TypingService
import im.vector.matrix.android.api.session.room.uploads.UploadsService
import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.Optional
/** /**
@@ -42,6 +43,7 @@ interface Room :
TypingService, TypingService,
MembershipService, MembershipService,
StateService, StateService,
UploadsService,
ReportingService, ReportingService,
RelationService, RelationService,
RoomCryptoService, RoomCryptoService,

View File

@@ -46,10 +46,10 @@ data class RoomSummary constructor(
val readMarkerId: String? = null, val readMarkerId: String? = null,
val userDrafts: List<UserDraft> = emptyList(), val userDrafts: List<UserDraft> = emptyList(),
val isEncrypted: Boolean, val isEncrypted: Boolean,
val encryptionEventTs: Long?,
val inviterId: String? = null, val inviterId: String? = null,
val typingRoomMemberIds: List<String> = emptyList(), val typingRoomMemberIds: List<String> = emptyList(),
val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS, val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
// TODO Plug it
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
) { ) {

View File

@@ -24,7 +24,7 @@ data class AudioInfo(
/** /**
* The mimetype of the audio e.g. "audio/aac". * The mimetype of the audio e.g. "audio/aac".
*/ */
@Json(name = "mimetype") val mimeType: String, @Json(name = "mimetype") val mimeType: String?,
/** /**
* The size of the audio clip in bytes. * The size of the audio clip in bytes.

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 im.vector.matrix.android.api.session.room.model.message
interface MessageContentWithFormattedBody : MessageContent {
/**
* The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
*/
val format: String?
/**
* The formatted version of the body. This is required if format is specified.
*/
val formattedBody: String?
/**
* Get the formattedBody, only if not blank and if the format is equal to "org.matrix.custom.html"
*/
val matrixFormattedBody: String?
get() = formattedBody?.takeIf { it.isNotBlank() && format == MessageFormat.FORMAT_MATRIX_HTML }
}

View File

@@ -34,15 +34,15 @@ data class MessageEmoteContent(
@Json(name = "body") override val body: String, @Json(name = "body") override val body: String,
/** /**
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported. * The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
*/ */
@Json(name = "format") val format: String? = null, @Json(name = "format") override val format: String? = null,
/** /**
* The formatted version of the body. This is required if format is specified. * The formatted version of the body. This is required if format is specified.
*/ */
@Json(name = "formatted_body") val formattedBody: String? = null, @Json(name = "formatted_body") override val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null @Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent ) : MessageContentWithFormattedBody

View File

@@ -29,7 +29,8 @@ data class MessageLocationContent(
@Json(name = "msgtype") override val msgType: String, @Json(name = "msgtype") override val msgType: String,
/** /**
* Required. A description of the location e.g. 'Big Ben, London, UK', or some kind of content description for accessibility e.g. 'location attachment'. * Required. A description of the location e.g. 'Big Ben, London, UK', or some kind
* of content description for accessibility e.g. 'location attachment'.
*/ */
@Json(name = "body") override val body: String, @Json(name = "body") override val body: String,

View File

@@ -34,15 +34,15 @@ data class MessageNoticeContent(
@Json(name = "body") override val body: String, @Json(name = "body") override val body: String,
/** /**
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported. * The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
*/ */
@Json(name = "format") val format: String? = null, @Json(name = "format") override val format: String? = null,
/** /**
* The formatted version of the body. This is required if format is specified. * The formatted version of the body. This is required if format is specified.
*/ */
@Json(name = "formatted_body") val formattedBody: String? = null, @Json(name = "formatted_body") override val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null @Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent ) : MessageContentWithFormattedBody

View File

@@ -34,15 +34,15 @@ data class MessageTextContent(
@Json(name = "body") override val body: String, @Json(name = "body") override val body: String,
/** /**
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported. * The format used in the formatted_body. Currently only "org.matrix.custom.html" is supported.
*/ */
@Json(name = "format") val format: String? = null, @Json(name = "format") override val format: String? = null,
/** /**
* The formatted version of the body. This is required if format is specified. * The formatted version of the body. This is required if format is specified.
*/ */
@Json(name = "formatted_body") val formattedBody: String? = null, @Json(name = "formatted_body") override val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null @Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent ) : MessageContentWithFormattedBody

View File

@@ -39,5 +39,5 @@ data class ThumbnailInfo(
/** /**
* The mimetype of the image, e.g. "image/jpeg". * The mimetype of the image, e.g. "image/jpeg".
*/ */
@Json(name = "mimetype") val mimeType: String @Json(name = "mimetype") val mimeType: String?
) )

View File

@@ -0,0 +1,34 @@
/*
* 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 im.vector.matrix.android.api.session.room.sender
data class SenderInfo(
val userId: String,
/**
* Consider using [disambiguatedDisplayName]
*/
val displayName: String?,
val isUniqueDisplayName: Boolean,
val avatarUrl: String?
) {
val disambiguatedDisplayName: String
get() = when {
displayName.isNullOrBlank() -> userId
isUniqueDisplayName -> displayName
else -> "$displayName ($userId)"
}
}

View File

@@ -58,7 +58,7 @@ interface Timeline {
/** /**
* Check if the timeline can be enriched by paginating. * Check if the timeline can be enriched by paginating.
* @param the direction to check in * @param direction the direction to check in
* @return true if timeline can be enriched * @return true if timeline can be enriched
*/ */
fun hasMoreToLoad(direction: Direction): Boolean fun hasMoreToLoad(direction: Direction): Boolean
@@ -104,6 +104,7 @@ interface Timeline {
interface Listener { interface Listener {
/** /**
* Call when the timeline has been updated through pagination or sync. * Call when the timeline has been updated through pagination or sync.
* The latest event is the first in the list
* @param snapshot the most up to date snapshot * @param snapshot the most up to date snapshot
*/ */
fun onTimelineUpdated(snapshot: List<TimelineEvent>) fun onTimelineUpdated(snapshot: List<TimelineEvent>)

View File

@@ -16,6 +16,7 @@
package im.vector.matrix.android.api.session.room.timeline package im.vector.matrix.android.api.session.room.timeline
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.RelationType import im.vector.matrix.android.api.session.events.model.RelationType
@@ -25,6 +26,7 @@ import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageStickerContent import im.vector.matrix.android.api.session.room.model.message.MessageStickerContent
import im.vector.matrix.android.api.session.room.model.message.isReply import im.vector.matrix.android.api.session.room.model.message.isReply
import im.vector.matrix.android.api.session.room.sender.SenderInfo
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
@@ -38,13 +40,17 @@ data class TimelineEvent(
val localId: Long, val localId: Long,
val eventId: String, val eventId: String,
val displayIndex: Int, val displayIndex: Int,
val senderName: String?, val senderInfo: SenderInfo,
val isUniqueDisplayName: Boolean,
val senderAvatar: String?,
val annotations: EventAnnotationsSummary? = null, val annotations: EventAnnotationsSummary? = null,
val readReceipts: List<ReadReceipt> = emptyList() val readReceipts: List<ReadReceipt> = emptyList()
) { ) {
init {
if (BuildConfig.DEBUG) {
assert(eventId == root.eventId)
}
}
val metadata = HashMap<String, Any>() val metadata = HashMap<String, Any>()
/** /**
@@ -62,14 +68,6 @@ data class TimelineEvent(
} }
} }
fun getDisambiguatedDisplayName(): String {
return when {
senderName.isNullOrBlank() -> root.senderId ?: ""
isUniqueDisplayName -> senderName
else -> "$senderName (${root.senderId})"
}
}
/** /**
* Get the metadata associated with a key. * Get the metadata associated with a key.
* @param key the key to get the metadata * @param key the key to get the metadata

View File

@@ -28,6 +28,10 @@ data class TimelineSettings(
* A flag to filter edit events * A flag to filter edit events
*/ */
val filterEdits: Boolean = false, val filterEdits: Boolean = false,
/**
* A flag to filter redacted events
*/
val filterRedacted: Boolean = false,
/** /**
* A flag to filter by types. It should be used with [allowedTypes] field * A flag to filter by types. It should be used with [allowedTypes] field
*/ */

View File

@@ -0,0 +1,26 @@
/*
* 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 im.vector.matrix.android.api.session.room.uploads
data class GetUploadsResult(
// List of fetched Events, most recent first
val uploadEvents: List<UploadEvent>,
// token to get more events
val nextToken: String,
// True if there are more event to load
val hasMore: Boolean
)

View File

@@ -0,0 +1,32 @@
/*
* 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 im.vector.matrix.android.api.session.room.uploads
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.message.MessageWithAttachmentContent
import im.vector.matrix.android.api.session.room.sender.SenderInfo
/**
* Wrapper around on Event.
* Similar to [im.vector.matrix.android.api.session.room.timeline.TimelineEvent], contains an Event with extra useful data
*/
data class UploadEvent(
val root: Event,
val eventId: String,
val contentWithAttachmentContent: MessageWithAttachmentContent,
val senderInfo: SenderInfo
)

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 im.vector.matrix.android.api.session.room.uploads
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
/**
* This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level.
*/
interface UploadsService {
/**
* Get a list of events containing URL sent to a room, from most recent to oldest one
* @param numberOfEvents the expected number of events to retrieve. The result can contain less events.
* @param since token to get next page, or null to get the first page
*/
fun getUploads(numberOfEvents: Int,
since: String?,
callback: MatrixCallback<GetUploadsResult>): Cancelable
}

View File

@@ -19,3 +19,7 @@ package im.vector.matrix.android.api.session.securestorage
interface KeySigner { interface KeySigner {
fun sign(canonicalJson: String): Map<String, Map<String, String>>? fun sign(canonicalJson: String): Map<String, Map<String, String>>?
} }
class EmptyKeySigner : KeySigner {
override fun sign(canonicalJson: String): Map<String, Map<String, String>>? = null
}

View File

@@ -35,12 +35,14 @@ interface SharedSecretStorageService {
* Use the SsssKeyCreationInfo object returned by the callback to get more information about the created key (recovery key ...) * Use the SsssKeyCreationInfo object returned by the callback to get more information about the created key (recovery key ...)
* *
* @param keyId the ID of the key * @param keyId the ID of the key
* @param key keep null if you want to generate a random key
* @param keyName a human readable name * @param keyName a human readable name
* @param keySigner Used to add a signature to the key (client should check key signature before storing secret) * @param keySigner Used to add a signature to the key (client should check key signature before storing secret)
* *
* @param callback Get key creation info * @param callback Get key creation info
*/ */
fun generateKey(keyId: String, fun generateKey(keyId: String,
key: SsssKeySpec?,
keyName: String, keyName: String,
keySigner: KeySigner?, keySigner: KeySigner?,
callback: MatrixCallback<SsssKeyCreationInfo>) callback: MatrixCallback<SsssKeyCreationInfo>)
@@ -111,6 +113,8 @@ interface SharedSecretStorageService {
fun checkShouldBeAbleToAccessSecrets(secretNames: List<String>, keyId: String?) : IntegrityResult fun checkShouldBeAbleToAccessSecrets(secretNames: List<String>, keyId: String?) : IntegrityResult
fun requestSecret(name: String, myOtherDeviceId: String)
data class KeyRef( data class KeyRef(
val keyId: String?, val keyId: String?,
val keySpec: SsssKeySpec? val keySpec: SsssKeySpec?

View File

@@ -19,5 +19,6 @@ package im.vector.matrix.android.api.session.securestorage
data class SsssKeyCreationInfo( data class SsssKeyCreationInfo(
val keyId: String = "", val keyId: String = "",
var content: SecretStorageKeyContent?, var content: SecretStorageKeyContent?,
val recoveryKey: String = "" val recoveryKey: String = "",
val keySpec: SsssKeySpec
) )

View File

@@ -0,0 +1,24 @@
/*
* 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 im.vector.matrix.android.api.session.terms
import im.vector.matrix.android.internal.session.terms.TermsResponse
data class GetTermsResponse(
val serverResponse: TermsResponse,
val alreadyAcceptedTermUrls: Set<String>
)

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 im.vector.matrix.android.api.session.terms
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
interface TermsService {
enum class ServiceType {
IntegrationManager,
IdentityService
}
fun getTerms(serviceType: ServiceType,
baseUrl: String,
callback: MatrixCallback<GetTermsResponse>): Cancelable
fun agreeToTerms(serviceType: ServiceType,
baseUrl: String,
agreedUrls: List<String>,
token: String?,
callback: MatrixCallback<Unit>): Cancelable
}

View File

@@ -61,9 +61,10 @@ interface UserService {
/** /**
* Observe a live [PagedList] of users sorted alphabetically. You can filter the users. * Observe a live [PagedList] of users sorted alphabetically. You can filter the users.
* @param filter the filter. It will look into userId and displayName. * @param filter the filter. It will look into userId and displayName.
* @param excludedUserIds userId list which will be excluded from the result list.
* @return a Livedata of users * @return a Livedata of users
*/ */
fun getPagedUsersLive(filter: String? = null): LiveData<PagedList<User>> fun getPagedUsersLive(filter: String? = null, excludedUserIds: Set<String>? = null): LiveData<PagedList<User>>
/** /**
* Get list of ignored users * Get list of ignored users

View File

@@ -22,6 +22,9 @@ package im.vector.matrix.android.api.session.user.model
*/ */
data class User( data class User(
val userId: String, val userId: String,
/**
* For usage in UI, consider using [getBestName]
*/
val displayName: String? = null, val displayName: String? = null,
val avatarUrl: String? = null val avatarUrl: String? = null
) { ) {

View File

@@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.session.room.sender.SenderInfo
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import java.util.Locale import java.util.Locale
@@ -154,3 +155,5 @@ fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlia
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl) fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl)
fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
fun SenderInfo.toMatrixItem() = MatrixItem.UserItem(userId, disambiguatedDisplayName, avatarUrl)

View File

@@ -25,12 +25,15 @@ import im.vector.matrix.android.internal.auth.db.AuthRealmMigration
import im.vector.matrix.android.internal.auth.db.AuthRealmModule import im.vector.matrix.android.internal.auth.db.AuthRealmModule
import im.vector.matrix.android.internal.auth.db.RealmPendingSessionStore import im.vector.matrix.android.internal.auth.db.RealmPendingSessionStore
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
import im.vector.matrix.android.internal.auth.login.DefaultDirectLoginTask
import im.vector.matrix.android.internal.auth.login.DirectLoginTask
import im.vector.matrix.android.internal.database.RealmKeysUtils import im.vector.matrix.android.internal.database.RealmKeysUtils
import im.vector.matrix.android.internal.di.AuthDatabase import im.vector.matrix.android.internal.di.AuthDatabase
import im.vector.matrix.android.internal.wellknown.WellknownModule
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import java.io.File import java.io.File
@Module @Module(includes = [WellknownModule::class])
internal abstract class AuthModule { internal abstract class AuthModule {
@Module @Module
@@ -59,14 +62,17 @@ internal abstract class AuthModule {
} }
@Binds @Binds
abstract fun bindSessionParamsStore(sessionParamsStore: RealmSessionParamsStore): SessionParamsStore abstract fun bindSessionParamsStore(store: RealmSessionParamsStore): SessionParamsStore
@Binds @Binds
abstract fun bindPendingSessionStore(pendingSessionStore: RealmPendingSessionStore): PendingSessionStore abstract fun bindPendingSessionStore(store: RealmPendingSessionStore): PendingSessionStore
@Binds @Binds
abstract fun bindAuthenticationService(authenticationService: DefaultAuthenticationService): AuthenticationService abstract fun bindAuthenticationService(service: DefaultAuthenticationService): AuthenticationService
@Binds @Binds
abstract fun bindSessionCreator(sessionCreator: DefaultSessionCreator): SessionCreator abstract fun bindSessionCreator(creator: DefaultSessionCreator): SessionCreator
@Binds
abstract fun bindDirectLoginTask(task: DefaultDirectLoginTask): DirectLoginTask
} }

View File

@@ -23,28 +23,33 @@ import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.data.LoginFlowResult import im.vector.matrix.android.api.auth.data.LoginFlowResult
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.auth.data.Versions import im.vector.matrix.android.api.auth.data.Versions
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
import im.vector.matrix.android.api.auth.data.isSupportedBySdk import im.vector.matrix.android.api.auth.data.isSupportedBySdk
import im.vector.matrix.android.api.auth.login.LoginWizard import im.vector.matrix.android.api.auth.login.LoginWizard
import im.vector.matrix.android.api.auth.registration.RegistrationWizard import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.api.auth.wellknown.WellknownResult
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.NoOpCancellable
import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
import im.vector.matrix.android.internal.auth.data.RiotConfig import im.vector.matrix.android.internal.auth.data.RiotConfig
import im.vector.matrix.android.internal.auth.db.PendingSessionData import im.vector.matrix.android.internal.auth.db.PendingSessionData
import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard
import im.vector.matrix.android.internal.auth.login.DirectLoginTask
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard
import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.RetrofitFactory import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.task.launchToCallback import im.vector.matrix.android.internal.task.launchToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.exhaustive
import im.vector.matrix.android.internal.util.toCancelable import im.vector.matrix.android.internal.util.toCancelable
import kotlinx.coroutines.GlobalScope import im.vector.matrix.android.internal.wellknown.GetWellknownTask
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@@ -59,7 +64,10 @@ internal class DefaultAuthenticationService @Inject constructor(
private val sessionParamsStore: SessionParamsStore, private val sessionParamsStore: SessionParamsStore,
private val sessionManager: SessionManager, private val sessionManager: SessionManager,
private val sessionCreator: SessionCreator, private val sessionCreator: SessionCreator,
private val pendingSessionStore: PendingSessionStore private val pendingSessionStore: PendingSessionStore,
private val getWellknownTask: GetWellknownTask,
private val directLoginTask: DirectLoginTask,
private val taskExecutor: TaskExecutor
) : AuthenticationService { ) : AuthenticationService {
private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -78,14 +86,21 @@ internal class DefaultAuthenticationService @Inject constructor(
} }
} }
override fun getSession(sessionParams: SessionParams): Session? { override fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable {
return sessionManager.getOrCreateSession(sessionParams) val homeServerConnectionConfig = sessionParamsStore.get(sessionId)?.homeServerConnectionConfig
return if (homeServerConnectionConfig == null) {
callback.onFailure(IllegalStateException("Session not found"))
NoOpCancellable
} else {
getLoginFlow(homeServerConnectionConfig, callback)
}
} }
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable { override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
pendingSessionData = null pendingSessionData = null
return GlobalScope.launch(coroutineDispatchers.main) { return taskExecutor.executorScope.launch(coroutineDispatchers.main) {
pendingSessionStore.delete() pendingSessionStore.delete()
val result = runCatching { val result = runCatching {
@@ -148,27 +163,71 @@ internal class DefaultAuthenticationService @Inject constructor(
val authAPI = buildAuthAPI(homeServerConnectionConfig) val authAPI = buildAuthAPI(homeServerConnectionConfig)
// Ok, try to get the config.json file of a RiotWeb client // Ok, try to get the config.json file of a RiotWeb client
val riotConfig = executeRequest<RiotConfig>(null) { return runCatching {
apiCall = authAPI.getRiotConfig() executeRequest<RiotConfig>(null) {
} apiCall = authAPI.getRiotConfig()
if (riotConfig.defaultHomeServerUrl?.isNotBlank() == true) {
// Ok, good sign, we got a default hs url
val newHomeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = Uri.parse(riotConfig.defaultHomeServerUrl)
)
val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig)
val versions = executeRequest<Versions>(null) {
apiCall = newAuthAPI.versions()
} }
return getLoginFlowResult(newAuthAPI, versions, riotConfig.defaultHomeServerUrl)
} else {
// Config exists, but there is no default homeserver url (ex: https://riot.im/app)
throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */)
} }
.map { riotConfig ->
if (riotConfig.defaultHomeServerUrl?.isNotBlank() == true) {
// Ok, good sign, we got a default hs url
val newHomeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = Uri.parse(riotConfig.defaultHomeServerUrl)
)
val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig)
val versions = executeRequest<Versions>(null) {
apiCall = newAuthAPI.versions()
}
getLoginFlowResult(newAuthAPI, versions, riotConfig.defaultHomeServerUrl)
} else {
// Config exists, but there is no default homeserver url (ex: https://riot.im/app)
throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */)
}
}
.fold(
{
it
},
{
if (it is Failure.OtherServerError
&& it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) {
// Try with wellknown
getWellknownLoginFlowInternal(homeServerConnectionConfig)
} else {
throw it
}
}
)
}
private suspend fun getWellknownLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult {
val domain = homeServerConnectionConfig.homeServerUri.host
?: throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */)
// Create a fake userId, for the getWellknown task
val fakeUserId = "@alice:$domain"
val wellknownResult = getWellknownTask.execute(GetWellknownTask.Params(fakeUserId))
return when (wellknownResult) {
is WellknownResult.Prompt -> {
val newHomeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = Uri.parse(wellknownResult.homeServerUrl),
identityServerUri = wellknownResult.identityServerUrl?.let { Uri.parse(it) }
)
val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig)
val versions = executeRequest<Versions>(null) {
apiCall = newAuthAPI.versions()
}
getLoginFlowResult(newAuthAPI, versions, wellknownResult.homeServerUrl)
}
else -> throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */)
}.exhaustive
} }
private suspend fun getLoginFlowResult(authAPI: AuthAPI, versions: Versions, homeServerUrl: String): LoginFlowResult { private suspend fun getLoginFlowResult(authAPI: AuthAPI, versions: Versions, homeServerUrl: String): LoginFlowResult {
@@ -193,7 +252,8 @@ internal class DefaultAuthenticationService @Inject constructor(
retrofitFactory, retrofitFactory,
coroutineDispatchers, coroutineDispatchers,
sessionCreator, sessionCreator,
pendingSessionStore pendingSessionStore,
taskExecutor.executorScope
).also { ).also {
currentRegistrationWizard = it currentRegistrationWizard = it
} }
@@ -213,7 +273,8 @@ internal class DefaultAuthenticationService @Inject constructor(
retrofitFactory, retrofitFactory,
coroutineDispatchers, coroutineDispatchers,
sessionCreator, sessionCreator,
pendingSessionStore pendingSessionStore,
taskExecutor.executorScope
).also { ).also {
currentLoginWizard = it currentLoginWizard = it
} }
@@ -230,7 +291,7 @@ internal class DefaultAuthenticationService @Inject constructor(
pendingSessionData = pendingSessionData?.homeServerConnectionConfig pendingSessionData = pendingSessionData?.homeServerConnectionConfig
?.let { PendingSessionData(it) } ?.let { PendingSessionData(it) }
.also { .also {
GlobalScope.launch(coroutineDispatchers.main) { taskExecutor.executorScope.launch(coroutineDispatchers.main) {
if (it == null) { if (it == null) {
// Should not happen // Should not happen
pendingSessionStore.delete() pendingSessionStore.delete()
@@ -247,7 +308,7 @@ internal class DefaultAuthenticationService @Inject constructor(
pendingSessionData = null pendingSessionData = null
GlobalScope.launch(coroutineDispatchers.main) { taskExecutor.executorScope.launch(coroutineDispatchers.main) {
pendingSessionStore.delete() pendingSessionStore.delete()
} }
} }
@@ -255,11 +316,31 @@ internal class DefaultAuthenticationService @Inject constructor(
override fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, override fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
credentials: Credentials, credentials: Credentials,
callback: MatrixCallback<Session>): Cancelable { callback: MatrixCallback<Session>): Cancelable {
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
createSessionFromSso(credentials, homeServerConnectionConfig) createSessionFromSso(credentials, homeServerConnectionConfig)
} }
} }
override fun getWellKnownData(matrixId: String, callback: MatrixCallback<WellknownResult>): Cancelable {
return getWellknownTask
.configureWith(GetWellknownTask.Params(matrixId)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
override fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig,
matrixId: String,
password: String,
initialDeviceName: String,
callback: MatrixCallback<Session>): Cancelable {
return directLoginTask
.configureWith(DirectLoginTask.Params(homeServerConnectionConfig, matrixId, password, initialDeviceName)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
private suspend fun createSessionFromSso(credentials: Credentials, private suspend fun createSessionFromSso(credentials: Credentials,
homeServerConnectionConfig: HomeServerConnectionConfig): Session = withContext(coroutineDispatchers.computation) { homeServerConnectionConfig: HomeServerConnectionConfig): Session = withContext(coroutineDispatchers.computation) {
sessionCreator.createSession(credentials, homeServerConnectionConfig) sessionCreator.createSession(credentials, homeServerConnectionConfig)

View File

@@ -46,14 +46,14 @@ internal class DefaultSessionCreator @Inject constructor(
val sessionParams = SessionParams( val sessionParams = SessionParams(
credentials = credentials, credentials = credentials,
homeServerConnectionConfig = homeServerConnectionConfig.copy( homeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = credentials.wellKnown?.homeServer?.baseURL homeServerUri = credentials.discoveryInformation?.homeServer?.baseURL
// remove trailing "/" // remove trailing "/"
?.trim { it == '/' } ?.trim { it == '/' }
?.takeIf { it.isNotBlank() } ?.takeIf { it.isNotBlank() }
?.also { Timber.d("Overriding homeserver url to $it") } ?.also { Timber.d("Overriding homeserver url to $it") }
?.let { Uri.parse(it) } ?.let { Uri.parse(it) }
?: homeServerConnectionConfig.homeServerUri, ?: homeServerConnectionConfig.homeServerUri,
identityServerUri = credentials.wellKnown?.identityServer?.baseURL identityServerUri = credentials.discoveryInformation?.identityServer?.baseURL
// remove trailing "/" // remove trailing "/"
?.trim { it == '/' } ?.trim { it == '/' }
?.takeIf { it.isNotBlank() } ?.takeIf { it.isNotBlank() }

View File

@@ -51,7 +51,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
} }
return SessionParamsEntity( return SessionParamsEntity(
sessionParams.credentials.sessionId(), sessionParams.credentials.sessionId(),
sessionParams.credentials.userId, sessionParams.userId,
credentialsJson, credentialsJson,
homeServerConnectionConfigJson, homeServerConnectionConfigJson,
sessionParams.isTokenValid) sessionParams.isTokenValid)

View File

@@ -38,7 +38,7 @@ import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.launchToCallback import im.vector.matrix.android.internal.task.launchToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@@ -47,7 +47,8 @@ internal class DefaultLoginWizard(
retrofitFactory: RetrofitFactory, retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionCreator: SessionCreator, private val sessionCreator: SessionCreator,
private val pendingSessionStore: PendingSessionStore private val pendingSessionStore: PendingSessionStore,
private val coroutineScope: CoroutineScope
) : LoginWizard { ) : LoginWizard {
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here") private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
@@ -59,7 +60,7 @@ internal class DefaultLoginWizard(
password: String, password: String,
deviceName: String, deviceName: String,
callback: MatrixCallback<Session>): Cancelable { callback: MatrixCallback<Session>): Cancelable {
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
loginInternal(login, password, deviceName) loginInternal(login, password, deviceName)
} }
} }
@@ -80,7 +81,7 @@ internal class DefaultLoginWizard(
} }
override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable { override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
resetPasswordInternal(email, newPassword) resetPasswordInternal(email, newPassword)
} }
} }
@@ -108,7 +109,7 @@ internal class DefaultLoginWizard(
callback.onFailure(IllegalStateException("developer error, no reset password in progress")) callback.onFailure(IllegalStateException("developer error, no reset password in progress"))
return NoOpCancellable return NoOpCancellable
} }
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
resetPasswordMailConfirmedInternal(safeResetPasswordData) resetPasswordMailConfirmedInternal(safeResetPasswordData)
} }
} }

View File

@@ -0,0 +1,61 @@
/*
* 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 im.vector.matrix.android.internal.auth.login
import dagger.Lazy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.internal.auth.AuthAPI
import im.vector.matrix.android.internal.auth.SessionCreator
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import okhttp3.OkHttpClient
import javax.inject.Inject
internal interface DirectLoginTask : Task<DirectLoginTask.Params, Session> {
data class Params(
val homeServerConnectionConfig: HomeServerConnectionConfig,
val userId: String,
val password: String,
val deviceName: String
)
}
internal class DefaultDirectLoginTask @Inject constructor(
@Unauthenticated
private val okHttpClient: Lazy<OkHttpClient>,
private val retrofitFactory: RetrofitFactory,
private val sessionCreator: SessionCreator
) : DirectLoginTask {
override suspend fun execute(params: DirectLoginTask.Params): Session {
val authAPI = retrofitFactory.create(okHttpClient, params.homeServerConnectionConfig.homeServerUri.toString())
.create(AuthAPI::class.java)
val loginParams = PasswordLoginParams.userIdentifier(params.userId, params.password, params.deviceName)
val credentials = executeRequest<Credentials>(null) {
apiCall = authAPI.login(loginParams)
}
return sessionCreator.createSession(credentials, params.homeServerConnectionConfig)
}
}

View File

@@ -33,7 +33,7 @@ import im.vector.matrix.android.internal.auth.db.PendingSessionData
import im.vector.matrix.android.internal.network.RetrofitFactory import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.task.launchToCallback import im.vector.matrix.android.internal.task.launchToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@@ -45,7 +45,8 @@ internal class DefaultRegistrationWizard(
private val retrofitFactory: RetrofitFactory, private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionCreator: SessionCreator, private val sessionCreator: SessionCreator,
private val pendingSessionStore: PendingSessionStore private val pendingSessionStore: PendingSessionStore,
private val coroutineScope: CoroutineScope
) : RegistrationWizard { ) : RegistrationWizard {
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here") private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
@@ -72,7 +73,7 @@ internal class DefaultRegistrationWizard(
override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable { override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable {
val params = RegistrationParams() val params = RegistrationParams()
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
performRegistrationRequest(params) performRegistrationRequest(params)
} }
} }
@@ -86,7 +87,7 @@ internal class DefaultRegistrationWizard(
password = password, password = password,
initialDeviceDisplayName = initialDeviceDisplayName initialDeviceDisplayName = initialDeviceDisplayName
) )
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
performRegistrationRequest(params) performRegistrationRequest(params)
.also { .also {
pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true) pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true)
@@ -101,7 +102,7 @@ internal class DefaultRegistrationWizard(
return NoOpCancellable return NoOpCancellable
} }
val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response)) val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response))
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
performRegistrationRequest(params) performRegistrationRequest(params)
} }
} }
@@ -112,13 +113,13 @@ internal class DefaultRegistrationWizard(
return NoOpCancellable return NoOpCancellable
} }
val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession)) val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession))
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
performRegistrationRequest(params) performRegistrationRequest(params)
} }
} }
override fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable { override fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable {
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
pendingSessionData = pendingSessionData.copy(currentThreePidData = null) pendingSessionData = pendingSessionData.copy(currentThreePidData = null)
.also { pendingSessionStore.savePendingSessionData(it) } .also { pendingSessionStore.savePendingSessionData(it) }
@@ -131,7 +132,7 @@ internal class DefaultRegistrationWizard(
callback.onFailure(IllegalStateException("developer error, call createAccount() method first")) callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
return NoOpCancellable return NoOpCancellable
} }
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
sendThreePid(safeCurrentThreePid) sendThreePid(safeCurrentThreePid)
} }
} }
@@ -177,13 +178,13 @@ internal class DefaultRegistrationWizard(
callback.onFailure(IllegalStateException("developer error, no pending three pid")) callback.onFailure(IllegalStateException("developer error, no pending three pid"))
return NoOpCancellable return NoOpCancellable
} }
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
performRegistrationRequest(safeParam, delayMillis) performRegistrationRequest(safeParam, delayMillis)
} }
} }
override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable { override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
validateThreePid(code) validateThreePid(code)
} }
} }
@@ -199,7 +200,7 @@ internal class DefaultRegistrationWizard(
code = code code = code
) )
val validationResponse = validateCodeTask.execute(ValidateCodeTask.Params(url, validationBody)) val validationResponse = validateCodeTask.execute(ValidateCodeTask.Params(url, validationBody))
if (validationResponse.success == true) { if (validationResponse.isSuccess()) {
// The entered code is correct // The entered code is correct
// Same than validate email // Same than validate email
return performRegistrationRequest(registrationParams, 3_000) return performRegistrationRequest(registrationParams, 3_000)
@@ -214,7 +215,7 @@ internal class DefaultRegistrationWizard(
callback.onFailure(IllegalStateException("developer error, call createAccount() method first")) callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
return NoOpCancellable return NoOpCancellable
} }
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession)) val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession))
performRegistrationRequest(params) performRegistrationRequest(params)
} }

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