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

Compare commits

..

996 Commits

Author SHA1 Message Date
Benoit Marty
128f3493b7 Merge branch 'release/0.17.0' 2020-02-27 12:32:36 +01:00
Benoit Marty
495e4792b1 Prepare release 0.17.0 2020-02-27 12:32:03 +01:00
Benoit Marty
f906fc894b Sync Strings with Riot 2020-02-27 11:59:39 +01:00
Benoit Marty
6e060f63c6 Try to decode QrCode data in the debug Activity 2020-02-27 11:32:59 +01:00
Benoit Marty
9c7f5251b0 Merge pull request #1084 from vector-im/feature/room_key_share_crash
Fix crash after rework. RoomKeyShare is now an interface
2020-02-26 20:45:50 +01:00
Benoit Marty
a296234163 Fix crash after rework. RoomKeyShare is now an interface 2020-02-26 20:35:34 +01:00
Benoit Marty
a4d7b79e3c Merge pull request #1083 from vector-im/feature/last_fixies
Last fixes before release
2020-02-26 18:37:21 +01:00
Benoit Marty
3ac2296464 Disable freestyle crop, usability was not easy 2020-02-26 17:46:33 +01:00
Benoit Marty
b5a402c1e1 Fix crash when editing image 2020-02-26 17:45:39 +01:00
Benoit Marty
b8eeede5fd Safer code 2020-02-26 17:28:27 +01:00
Benoit Marty
d3e7e7e109 Make the small avatar also clickable and little change in the API to factorize code 2020-02-26 17:17:39 +01:00
Benoit Marty
a3af0be103 prevent wildcard for import 2020-02-26 17:01:38 +01:00
Benoit Marty
6349f9828d Optimize import 2020-02-26 16:59:20 +01:00
Benoit Marty
989d396c78 Rename package "sas" to "verification" 2020-02-26 16:52:16 +01:00
Benoit Marty
73ac7ab341 Compact code 2020-02-26 16:48:50 +01:00
Benoit Marty
a3133773a3 generic name for parameter 2020-02-26 16:45:12 +01:00
Benoit Marty
6e67d4749f Merge pull request #1082 from vector-im/feature/fix_rageshakes
Fix crashes reported by rageshakes
2020-02-26 16:10:47 +01:00
Benoit Marty
38c1ef3eb6 Fix lint issue 2020-02-26 16:08:34 +01:00
Benoit Marty
674450ef29 Merge pull request #1076 from vector-im/feature/fullscreen_avatar
Display avatar in fullscreen.
2020-02-26 16:06:13 +01:00
Benoit Marty
5e775298a7 Merge pull request #1081 from vector-im/feature/fix_redacted_event_format
Fix redacted event format on room overview.
2020-02-26 15:57:10 +01:00
onurays
93a687a172 Code review fixes. 2020-02-26 17:55:23 +03:00
Benoit Marty
4ba8a42977 Fix crash reported from rageshake 2020-02-26 15:32:52 +01:00
Benoit Marty
054d7668ef Fix crash reported from rageshake 2020-02-26 15:19:47 +01:00
Benoit Marty
a13fec84b0 Fix crash reported from rageshake 2020-02-26 15:00:25 +01:00
Benoit Marty
4bb3fb128f Fix crash reported from rageshake 2020-02-26 14:54:36 +01:00
onurays
04726a1ace Fix redacted event format on room overview.
Fixes #758
2020-02-26 16:51:01 +03:00
Benoit Marty
ccf72ffaa7 Fix crash reported from rageshake 2020-02-26 14:50:01 +01:00
Onuray Sahin
d2efe0e10c Merge branch 'develop' into feature/fullscreen_avatar 2020-02-26 16:23:02 +03:00
Benoit Marty
054b467caf Fix crash reported from rageshake 2020-02-26 14:18:48 +01:00
Benoit Marty
4280bc0780 Fix crash reported from rageshake 2020-02-26 14:10:51 +01:00
Benoit Marty
e1ea742023 Fix crash reported from rageshake 2020-02-26 13:59:58 +01:00
Benoit Marty
b7cf7e06a7 Merge pull request #1015 from vector-im/feature/new_signin_passphrase
Feature/new signin passphrase
2020-02-26 13:56:31 +01:00
onurays
536dce7929 Code review fixes. 2020-02-26 15:30:03 +03:00
onurays
56fc91a8c3 Code review fixes. 2020-02-26 15:26:53 +03:00
Benoit Marty
6622e0daca Code quality 2020-02-26 12:43:41 +01:00
Benoit Marty
f35b0660ca Rename constant for code readability 2020-02-26 12:37:36 +01:00
Valere
9d1718cda8 post merge clean 2020-02-26 12:20:03 +01:00
Valere
f7c128be3d Fix tests 2020-02-26 12:20:03 +01:00
Valere
c80fb52410 dead code 2020-02-26 12:20:03 +01:00
Valere
32389e1291 Remove wizard stuff 2020-02-26 12:20:03 +01:00
Valere
99f9a9817e Use human readable error (not technical) 2020-02-26 12:20:03 +01:00
Valere
b4a783198b code review 2020-02-26 12:20:03 +01:00
Valere
0cfc9451ca Cleaning 2020-02-26 12:20:03 +01:00
Valere
4b75baf772 Fix / trust was not properly updated after 4S restore 2020-02-26 12:20:03 +01:00
Valere
c579de1033 Fix / Test crash when crypto DB closed
Quick Work around
2020-02-26 12:20:03 +01:00
Valere
d537abc522 Fix / sign current device after entering xsigning passphrase 2020-02-26 12:20:03 +01:00
Valere
030f027516 Fixes #1051
XSigning | Self verification Empty bottomsheet
2020-02-26 12:20:03 +01:00
Valere
cbd7c1aa63 Fix / Bad ordering of active sessions 2020-02-26 12:20:03 +01:00
Valere
bdb1df75d4 Check trust on crypto thread 2020-02-26 12:20:03 +01:00
Valere
817dc19b9a Cleaning 2020-02-26 12:20:03 +01:00
Valere
f9be4fa2bd Fixes #1047 2020-02-26 12:20:03 +01:00
Valere
412aed6dcb Fix / Update title before return 2020-02-26 12:20:03 +01:00
Valere
9fc44c11de Fixes #937
Disabling bounds change animation, as it's messing with fragment change apparition and expanded state of bottomsheet
2020-02-26 12:20:03 +01:00
Valere
bf23094158 Fix / Green shield in conclusion title when verified from private keys 2020-02-26 12:20:03 +01:00
Valere
94d36e0c85 cleaning jni 2020-02-26 12:20:03 +01:00
Valere
0064934db9 Changed Encryption algorithm of 4S 2020-02-26 12:20:03 +01:00
Valere
e2e4ddf5ba Post rebase fix 2020-02-26 12:20:03 +01:00
Valere
9a08f5ec4e WIP / Verify from passphrase UX 2020-02-26 12:19:31 +01:00
Valere
cb669ad881 4S Activity WIP 2020-02-26 12:15:59 +01:00
Valere
3dc89c8d87 Update Self Verification BottomSheet for quads 2020-02-26 12:15:59 +01:00
Benoit Marty
cb73955946 Merge pull request #1050 from vector-im/feature/fix_some_perf
Feature/fix some perf
2020-02-26 12:02:36 +01:00
ganfra
bfe15555a1 Update changes 2020-02-26 11:51:05 +01:00
ganfra
bddeb6cb72 Clean files 2020-02-26 11:50:41 +01:00
ganfra
d57f6838e9 Remove decryption from room summary mapper and make TimelineEventDecryptor scoped to session 2020-02-26 11:50:41 +01:00
ganfra
cf8ffa3a7a Force trust task on crypto dispatcher to use cached realm from crypto store 2020-02-26 11:49:08 +01:00
Benoit Marty
40b4db4a64 Merge pull request #1060 from vector-im/feature/data_class_cleanup
Data class cleanup
2020-02-26 10:57:10 +01:00
Benoit Marty
246b39786c Merge pull request #1073 from vector-im/feature/improve_api
Improve api of CreateRoomParams
2020-02-26 10:55:48 +01:00
onurays
1a273407de Display avatar in fullscreen. 2020-02-26 12:12:49 +03:00
Benoit Marty
633f9d8522 ktlint 2020-02-26 09:55:57 +01:00
Benoit Marty
076ac23b38 Modification done by AndroidStudio 3.6 2020-02-26 07:59:54 +01:00
Benoit Marty
67180fd8db New direct chat: selecting several participants was not adding the room to the direct chats list 2020-02-26 07:38:10 +01:00
Benoit Marty
fc95bf4926 Improve CreateRoomParams API: update some API for better chaining of builder like methods (#1070) 2020-02-26 07:32:22 +01:00
Benoit Marty
e73f138151 Improve CreateRoomParams API: update Javadoc and ensure the return values will not be discarded (#1070) 2020-02-26 07:17:25 +01:00
Benoit Marty
30432cd3f4 Merge pull request #1069 from vector-im/feature/fix_dm
Fix dm chips
2020-02-25 19:28:46 +01:00
Benoit Marty
1072060cbb New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022) 2020-02-25 19:00:38 +01:00
Benoit Marty
a55e0f1af4 Fix crash - workaround 2020-02-25 18:17:40 +01:00
Benoit Marty
b95dfa4473 Create getBestName method for User 2020-02-25 18:14:02 +01:00
Benoit Marty
ec0cba2b23 Merge pull request #1067 from vector-im/feature/roomId
Display internal id of a room section in room profile
2020-02-25 12:22:14 +01:00
Benoit Marty
cfe9c4bb41 Display internal id of a room section in room profile, with copy to clipboard action, only in developer mode 2020-02-25 12:21:04 +01:00
Benoit Marty
68400cce03 Merge pull request #1049 from vector-im/feature/fix_invites
Feature/fix invites
2020-02-25 11:39:35 +01:00
Benoit Marty
17e028178e Code robustness (avoid using !!) 2020-02-25 11:20:11 +01:00
ganfra
19b9617a09 Invite: fix inviterId being overrided 2020-02-25 10:53:47 +01:00
Benoit Marty
08e4b4473c Disable manual rules now checked by ktlint 2020-02-25 09:57:12 +01:00
Benoit Marty
b9b921a4df Code quality 2020-02-25 09:53:17 +01:00
ganfra
483256093a Invite: fix room member not saved 2020-02-24 18:27:18 +01:00
Benoit Marty
d710a59554 Update change log 2020-02-24 17:44:51 +01:00
Benoit Marty
1023982858 Code readability 2020-02-24 17:40:28 +01:00
Benoit Marty
9c566b19f7 Ensure all eventId start with $, even ids for local echo 2020-02-24 17:35:43 +01:00
ganfra
b694721728 Update CHANGES 2020-02-24 17:33:40 +01:00
ganfra
8b0305c91d Clean code 2020-02-24 17:33:40 +01:00
ganfra
133d8d7f14 RoomList: fix joining/rejecting state 2020-02-24 17:33:40 +01:00
ganfra
9cdb1da614 Start fixing notif for invites 2020-02-24 17:33:40 +01:00
ganfra
981c9ac4ac Invite sync: assign eventId and remove the primaryKey constraint 2020-02-24 17:33:40 +01:00
Benoit Marty
3e8a0f7252 Merge pull request #1044 from vector-im/feature/fix_big_image_rotation
Use exif info of the image for correct rotation.
2020-02-24 17:30:33 +01:00
Benoit Marty
281620b88d Merge pull request #1048 from vector-im/feature/profile_share_actions
Share action is added to room profile and room member profile
2020-02-24 17:29:17 +01:00
Benoit Marty
c6b3b1e648 Fix bug on test 2020-02-24 17:27:04 +01:00
Benoit Marty
96af1957f9 Fix bug on progress step, found by integration test testBackupWithPassword() 2020-02-24 16:27:58 +01:00
Benoit Marty
b680e24938 Fix some tests 2020-02-24 16:16:37 +01:00
onurays
dec62a893e Code review fixes. 2020-02-24 18:06:59 +03:00
onurays
b3d4747d97 Code review fixes. 2020-02-24 17:35:57 +03:00
onurays
1124aa25fd Code review fixes. 2020-02-24 17:22:07 +03:00
Benoit Marty
b3c8b5526d Each session now have it's proper crypto store 2020-02-24 15:03:12 +01:00
Benoit Marty
1e44e77503 Make code robust to int received instead of boolean
https://github.com/matrix-org/synapse/issues/6977
2020-02-24 14:42:47 +01:00
Valere
964b611f4e Merge pull request #1053 from vector-im/feature/fix_712
Fixes #712
2020-02-24 14:07:35 +01:00
Valere
3e587af163 klint 2020-02-24 14:07:15 +01:00
Benoit Marty
b03b6bfc37 Fix crash discovered thanks to integration tests 2020-02-24 12:53:18 +01:00
Benoit Marty
1a6d4d0b03 Try to fix XSigningTest 2020-02-21 19:20:40 +01:00
Valere
c41661ece8 Fixes #712 2020-02-21 19:05:20 +01:00
Benoit Marty
1b763fb4c8 Cleanup XSigningTest 2020-02-21 18:52:36 +01:00
Benoit Marty
9ef267cb07 Cleanup after each test 2020-02-21 18:36:13 +01:00
Benoit Marty
d167ff9496 Make KeysBackup test pass (still work to do) 2020-02-21 17:58:04 +01:00
Benoit Marty
ccfa59ad31 Cleanup var -> val 2020-02-21 15:05:48 +01:00
onurays
f650e29ddd Share action is added to room profile and room member profile
Fixes #858
2020-02-21 15:30:01 +03:00
Onuray Sahin
1eefda18e4 Merge branch 'develop' into feature/fix_big_image_rotation 2020-02-21 13:13:02 +03:00
Benoit Marty
d6434654e2 Merge pull request #1043 from vector-im/feature/join_federation
Fix join room over federation
2020-02-21 10:08:05 +01:00
Benoit Marty
b6372df676 Merge pull request #1037 from vector-im/feature/crosssigning_qr
Migrate to binary QR code verification (#994)
2020-02-21 10:01:49 +01:00
Benoit Marty
d7de072155 typo 2020-02-21 09:58:56 +01:00
onurays
a11d70f173 Use exif info of the image for correct rotation. 2020-02-21 00:51:22 +03:00
Benoit Marty
a9909b37ba Update after Valere's review 2020-02-20 19:54:35 +01:00
Benoit Marty
3f9a5a4e54 Leaving a room creates a stuck "leaving room" loading screen. (#1041) 2020-02-20 19:31:33 +01:00
Benoit Marty
198e23c204 Second fix for #808 2020-02-20 19:23:32 +01:00
Benoit Marty
aefffc0c05 RoomPreviewNoPreviewFragment use room alias if there is no name 2020-02-20 19:19:31 +01:00
Benoit Marty
fd05fe36f5 Fix joining rooms from directory via federation isn't working. (#808) 2020-02-20 19:01:36 +01:00
Benoit Marty
b72698d63c Fix another issue when there is no name and no canonical alias on a public room 2020-02-20 18:47:07 +01:00
Benoit Marty
eec65fb622 Join room by roomId or room alias 2020-02-20 18:17:57 +01:00
Benoit Marty
87021dd6ec Rename field 2020-02-20 18:16:10 +01:00
Benoit Marty
b85c76b172 Simplify code 2020-02-20 18:05:53 +01:00
Benoit Marty
d867b1345f Format 2020-02-20 15:15:38 +01:00
Benoit Marty
80ef2192b9 Merge pull request #1036 from vector-im/feature/fix_key_restoring
Fix threading issues while restoring keys backup.
2020-02-20 15:13:13 +01:00
onurays
7d232527c8 Documentation of the API change is added. 2020-02-20 16:39:48 +03:00
Benoit Marty
e61d563589 Split long lines 2020-02-20 10:16:09 +01:00
Benoit Marty
74175ddfa0 Add a test and create extension for Byte to avoid using UByte (still experimental) 2020-02-20 10:12:07 +01:00
Benoit Marty
382fc6f05c Fix issue with long transactionId 2020-02-19 18:59:39 +01:00
Benoit Marty
932c478ed5 Change log 2020-02-19 18:11:23 +01:00
Benoit Marty
e81439d57b Remove v2 suffix 2020-02-19 18:09:33 +01:00
Benoit Marty
f81eb298cb Cleanup QRCode v1 2020-02-19 18:06:51 +01:00
Benoit Marty
859b9e4f8e Migrate to QrCode v2 - TODO: cleanup 2020-02-19 17:50:30 +01:00
Benoit Marty
e00d3ef63d QrCodeV2 WIP (al tests passing) 2020-02-19 17:34:07 +01:00
onurays
a97971dd84 Fix threading issues while restoring keys backup. 2020-02-19 18:30:12 +03:00
Benoit Marty
35b10daef1 Ensure RiotX is able to scan binary QrCodes 2020-02-19 14:26:45 +01:00
Benoit Marty
66a2958c39 Add unit test to check byte array to string conversion 2020-02-19 14:21:41 +01:00
Benoit Marty
571db7da55 Merge pull request #1035 from vector-im/feature/share_text
Fix share text to a single room issue
2020-02-19 10:08:29 +01:00
Benoit Marty
c6b231c0b1 Create toState() fun and use the enum 2020-02-18 14:12:50 +01:00
Benoit Marty
53410789c0 Cleanup 2020-02-18 14:12:50 +01:00
Benoit Marty
f8276e48e3 add kluent for test 2020-02-18 14:09:42 +01:00
Benoit Marty
86c38ebd2d Create Set.toggle() method 2020-02-18 13:33:49 +01:00
Benoit Marty
d730f96c41 Do not show alert when sharing text to a single room 2020-02-18 12:24:27 +01:00
Benoit Marty
7b3fcb7798 When sharing to a room save a draft to pre-fill the composer 2020-02-18 12:11:51 +01:00
Benoit Marty
26e8a60312 Merge pull request #1032 from vector-im/feature/crash
Fix crash in the room directory, when public room has no name (#1023)
2020-02-18 11:48:29 +01:00
Benoit Marty
7133d513b4 Fix crash in the room directory, when public room has no name (#1023) 2020-02-18 10:45:14 +01:00
Benoit Marty
8d1b2b35fd Merge pull request #1010 from vector-im/feature/attachment_process
Attachment process
2020-02-18 09:19:17 +01:00
Benoit Marty
b811bf9e7f Fix issue after merge 2020-02-17 23:21:08 +01:00
Benoit Marty
ebda12dd76 Merge branch 'develop' into feature/attachment_process 2020-02-17 19:22:50 +01:00
Benoit Marty
adc545a93d Merge pull request #1026 from vector-im/feature/crypto_service_renaming
Feature/crypto service renaming
2020-02-17 19:21:02 +01:00
Benoit Marty
fc740ae347 Merge pull request #1016 from vector-im/feature/cleanup_quadS
Cleanup quad s and AccountData service
2020-02-17 18:52:23 +01:00
Benoit Marty
67ed86fee5 Rename KeysBackup to DefaultKeysBackupService 2020-02-17 18:51:06 +01:00
Benoit Marty
f716e9d789 Fix compilation issue 2020-02-17 18:49:45 +01:00
Benoit Marty
141c7d6af0 Update comment 2020-02-17 18:43:04 +01:00
Benoit Marty
7495533edc Use let 2020-02-17 18:16:37 +01:00
Benoit Marty
f2f94c4a93 Reduce code duplication 2020-02-17 17:56:11 +01:00
Benoit Marty
4995c14f69 Add log 2020-02-17 17:46:15 +01:00
Benoit Marty
002e881704 Fix multiple share intent issue 2020-02-17 17:26:19 +01:00
Benoit Marty
51bbee297c Remove useless confirmation when shared data are previewables 2020-02-17 17:06:15 +01:00
Benoit Marty
82bd8a2e2a Ask for write permission before editing media 2020-02-17 16:29:10 +01:00
Benoit Marty
d67cd2cbef Small iteration on UI 2020-02-17 16:21:10 +01:00
ganfra
6d2025b61a Update CHANGES 2020-02-17 15:54:17 +01:00
ganfra
91cbcebf73 Make test compile 2020-02-17 15:50:42 +01:00
ganfra
fd0dceb597 Clean code 2020-02-17 15:23:24 +01:00
Benoit Marty
48a033b3bd Fix navigation issue when sharing text to multiple rooms 2020-02-17 14:58:20 +01:00
Benoit Marty
a3b205b310 No preview for elements sent from the keyboard 2020-02-17 14:48:12 +01:00
Benoit Marty
ca0cd9e97d Better code 2020-02-17 14:38:56 +01:00
Benoit Marty
31d5578063 Media preview: do not propose to edit Gif 2020-02-17 14:35:57 +01:00
Benoit Marty
b6f5e27155 Fix issue after merge of develop 2020-02-17 13:50:36 +01:00
Benoit Marty
13d3aa9ff1 Merge branch 'develop' into feature/attachment_process 2020-02-17 13:48:21 +01:00
Benoit Marty
629ecf786a Merge pull request #1024 from vector-im/feature/wording
Account creation: wrongly hints that an email can be used to create an account (#941)
2020-02-17 13:34:04 +01:00
Benoit Marty
ecf07ff64a Account creation: wrongly hints that an email can be used to create an account (#941) 2020-02-17 10:30:54 +01:00
ganfra
e349a35419 Crypto: expose cryptoService by a getter, removing the session implementation delegation 2020-02-16 15:40:31 +01:00
Benoit Marty
a61f508b5d Fix a nice bug 2020-02-14 22:00:36 +01:00
Benoit Marty
76085a4284 AccountData cleanup and Javadoc 2020-02-14 21:50:37 +01:00
Benoit Marty
446d826dd3 Create tag interface AccountDataContent 2020-02-14 20:53:45 +01:00
Benoit Marty
2d6f57e214 More cleanup/code lisibility 2020-02-14 20:50:21 +01:00
Ganard
a305ce302e Coroutine sequencer: use semaphore 2020-02-14 19:37:51 +01:00
Benoit Marty
7ddea99fc6 Move and improve withOlmDecryption() and withOlmEncryption() 2020-02-14 18:51:27 +01:00
Benoit Marty
4c3b754de4 Use const from DefaultSharedSecretStorageService 2020-02-14 18:33:15 +01:00
Benoit Marty
7878da25b8 Use doSync<>() to reduce boilerplate and add test checks
and more cleanup
2020-02-14 18:18:20 +01:00
Benoit Marty
01597a89dc Avoid code duplication 2020-02-14 17:53:27 +01:00
Valere
b04ee7153a Merge pull request #995 from vector-im/feature/4s_msc1946
Feature/4s msc1946
2020-02-14 17:49:16 +01:00
Benoit Marty
db19ab0531 Merge branch 'develop' into feature/4s_msc1946 2020-02-14 17:45:44 +01:00
Benoit Marty
cad818c341 ktlint 2020-02-14 17:05:14 +01:00
Benoit Marty
1660a0f846 Version++ 2020-02-14 15:16:00 +01:00
Benoit Marty
56677f0908 Merge branch 'release/0.16.0' 2020-02-14 15:14:55 +01:00
Benoit Marty
4987a75039 Merge branch 'release/0.16.0' into develop 2020-02-14 15:14:54 +01:00
Benoit Marty
e20d1724c9 Prepare release 0.16.0 2020-02-14 15:14:48 +01:00
Benoit Marty
f6f9c349ec Add entry in changelog for the 2 mistakes 2020-02-14 14:52:10 +01:00
Benoit Marty
88b8ceaeb5 Merge pull request #1005 from vector-im/feature/fix_event_redaction_prompt
Do not ask for a reason if user wants to delete his own message.
2020-02-14 14:36:01 +01:00
Benoit Marty
e4577d8fef Small cleanup before merge 2020-02-14 14:34:22 +01:00
onurays
030d6824e3 Code review fixes. 2020-02-14 15:04:25 +03:00
Benoit Marty
ab437e249d Update emoji for API change 2020-02-14 12:43:27 +01:00
Valere
35835be03e klint 2020-02-14 12:06:07 +01:00
Valere
f99eca8014 Code review 2020-02-14 10:48:18 +01:00
Valere
e0eede1150 cleaning 2020-02-14 10:48:18 +01:00
Valere
64647cb465 Fix / Save account data after update (local echo) 2020-02-14 10:48:18 +01:00
Valere
def01cca8f Fix test + changes 2020-02-14 10:48:18 +01:00
Valere
61ea4191dc Update json viewer lib 2020-02-14 10:48:18 +01:00
Valere
108ebea84e SSSS service + test 2020-02-14 10:48:18 +01:00
Valere
bf06b57bad Refactor Account Data
Auto stash before rebase of "develop"
2020-02-14 10:48:18 +01:00
Valere
a250a895fe Remove redudant calls to eventBus post 2020-02-14 10:47:34 +01:00
Benoit Marty
2c2bf61727 Update Changelog 2020-02-14 09:40:07 +01:00
Benoit Marty
6c0bddc893 Code quality 2020-02-13 23:20:35 +01:00
Benoit Marty
930d62c87b Restore Breadcrumbs comparator, we can maybe improve this later 2020-02-13 23:00:03 +01:00
Benoit Marty
b66b96899e Better Kotlin code 2020-02-13 22:56:11 +01:00
Benoit Marty
e6bd09859f Compress image before sending 2020-02-13 22:50:55 +01:00
Benoit Marty
385fa317c0 Add a green frame around current small preview 2020-02-13 20:45:03 +01:00
Benoit Marty
ecd547b86c Auto close the room picker and open the first room when data are shared in case of multi selection 2020-02-13 20:10:59 +01:00
Benoit Marty
d87b951403 Improve code 2020-02-13 20:10:59 +01:00
Benoit Marty
06ba478232 Send files to several rooms at a time 2020-02-13 20:10:59 +01:00
Benoit Marty
81de914360 Propose to edit media before sending, when coming form another application 2020-02-13 20:10:59 +01:00
Benoit Marty
b7ec495d6b Add comment 2020-02-13 19:52:11 +01:00
Benoit Marty
a8953568f3 Set custom colors 2020-02-13 19:52:11 +01:00
Benoit Marty
41b20863fb Mention uCrop library copyright 2020-02-13 19:52:11 +01:00
Benoit Marty
94340a49d7 Ask for permission 2020-02-13 19:52:11 +01:00
Benoit Marty
c67fd508e7 Cleanup 2020-02-13 19:52:11 +01:00
Ganard
eccc52fe13 Sharing: start extracting from RoomList as it's getting messy 2020-02-13 19:52:11 +01:00
Ganard
b7a7aa2f15 Attachment: continue working on preview screen 2020-02-13 19:52:11 +01:00
ganfra
6471787232 Share: start managing multi selection and warning 2020-02-13 19:52:11 +01:00
ganfra
41f1ec5d88 Attachments: preview with pager mode 2020-02-13 19:51:20 +01:00
Ganard
a26e959430 Attachements: introduce structure for preview, cropping and compressing 2020-02-13 19:51:20 +01:00
Benoit Marty
5808c1de22 TextView displaying emoji should have text color set to black 2020-02-13 19:50:31 +01:00
Benoit Marty
d21604b791 Merge pull request #989 from vector-im/feature/qr_code_mistake
Fix issue with verification when other client declares it can only show QR code (#988)
2020-02-13 19:41:10 +01:00
onurays
fd135e1eeb Compute message body for encrypted messages too. 2020-02-13 18:09:26 +03:00
onurays
f28e4cf991 Fix comparison of user ids. 2020-02-13 17:57:38 +03:00
Valere
1941862499 Fix / Remove debug code line 2020-02-13 15:50:08 +01:00
onurays
983593d647 getRedactionReason function is refactored. 2020-02-13 17:28:14 +03:00
onurays
1b413934b5 Set redaction reason as message body. 2020-02-13 16:42:13 +03:00
onurays
8a3e93ae96 Do not ask for a reason if user wants to delete his own message.
Fixes (#1003)
2020-02-13 14:59:41 +03:00
Benoit Marty
5191cbaf93 Merge pull request #1001 from vector-im/feature/pusher_service
Cleanup pusher service and improve API
2020-02-13 08:02:33 +01:00
Benoit Marty
f2e6900cfb Move throw line 2020-02-13 07:44:54 +01:00
Benoit Marty
0b7e757f3c Code lisibility: use generic name for parameters in Dagger modules 2020-02-13 07:12:11 +01:00
Benoit Marty
5b2c947af1 Rename internal class 2020-02-13 07:05:46 +01:00
Benoit Marty
6a69c6356d PushersService.pushers() has been renamed to PushersService.getPushers() 2020-02-13 07:04:54 +01:00
Benoit Marty
e492e4318b Do some parameter checks 2020-02-13 06:48:52 +01:00
Benoit Marty
93d38843c3 Add Javadoc 2020-02-13 06:36:21 +01:00
Benoit Marty
7d94519064 Merge pull request #998 from vector-im/feature/msc_2192_update_event_prefix
Use vendor prefix for non merged MSC
2020-02-12 22:41:12 +01:00
Valere
21bac0f867 Fix / typo in poll 2020-02-12 22:31:13 +01:00
Valere
32c4ad9ecb Use vendor prefix for non merged MSC 2020-02-12 19:09:23 +01:00
Valere
e1342d096b Merge pull request #810 from vector-im/feature/msc_2192
Feature/msc 2192
2020-02-12 17:41:50 +01:00
Valere
e91c6c216d code quality 2020-02-12 17:03:21 +01:00
Onuray Sahin
6013e1653b Open room member profile from reactions and read receipts. (#990)
Open room member profile from reactions and read receipts. Fixes #875
2020-02-12 15:57:49 +01:00
Benoit Marty
47f47e40c4 code review and cleanup 2020-02-12 15:09:38 +01:00
Benoit Marty
9016688aec Fix compilation issue after rebase 2020-02-12 13:48:08 +01:00
Valere
d5f8a0c0fc code quality 2020-02-12 13:40:54 +01:00
Valere
33c657bff8 Update change log 2020-02-12 13:40:54 +01:00
Valere
9a7bd35ddc Add m.buttons support (a.k.a bot buttons) 2020-02-12 13:40:54 +01:00
Valere
3ac54c51f6 cleaning 2020-02-12 13:39:30 +01:00
Valere
c4ea2507f8 Add command to send poll 2020-02-12 13:39:30 +01:00
Valere
6001ac60ab klint cleaning 2020-02-12 13:37:16 +01:00
Valere
0cea26ec77 ux tweak on poll result 2020-02-12 13:37:16 +01:00
Valere
3dae220501 Fix / filter edits and poll response form lastPreviewableMessage 2020-02-12 13:37:16 +01:00
Valere
577c5a16b3 Support incremental poll response aggregation + display 2020-02-12 12:47:28 +01:00
Valere
a0aebed3f7 Message Poll UX, and model 2020-02-12 12:42:17 +01:00
Benoit Marty
3a044bd655 Add Javadoc 2020-02-12 11:39:36 +01:00
Benoit Marty
6acfab3242 Rename VerificationListener to Listener 2020-02-12 11:39:36 +01:00
Benoit Marty
bf02746d87 Clenaup VerificationService.VerificationListener 2020-02-12 11:39:36 +01:00
Benoit Marty
377d944228 Cleanup API 2020-02-12 11:39:36 +01:00
Benoit Marty
6ff974b3ea Fix issue with verification when other client declares it can only show QR code (#988) 2020-02-12 11:39:36 +01:00
Onuray Sahin
7c5bb4ff5b Merge pull request #986 from vector-im/feature/fix_clear_cache_notification_click
Fix crash by removing all notifications after clearing cache.
2020-02-11 18:06:16 +03:00
onurays
3573aea600 Fix crash by removing all notifications after clearing cache.
Fixes #878
2020-02-11 16:48:28 +03:00
Benoit Marty
251de0b89c Merge pull request #982 from vector-im/feature/remove_message_step2
Redact message step2
2020-02-11 11:35:16 +01:00
Benoit Marty
6fce2a3066 Change wording adding "..." because there is now confirmation dialog
And make the item red because it is destructive. Also use "redact" terminology
2020-02-11 11:04:21 +01:00
Benoit Marty
b40da4ef0f Disable TextInputLayout instead of TextInputEditText 2020-02-11 11:00:33 +01:00
Benoit Marty
96341df5e7 Format layout 2020-02-11 11:00:01 +01:00
Benoit Marty
7ccc1c559c Merge pull request #977 from vector-im/feature/event_deletion_dialog
Show confirmation dialog before deleting a message
2020-02-11 10:45:17 +01:00
Benoit Marty
35ed22ab2b improve script 2020-02-11 10:38:13 +01:00
onurays
afbd9cff70 Merge branch 'develop' into feature/event_deletion_dialog
# Conflicts:
#	CHANGES.md
#	vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
2020-02-11 12:11:36 +03:00
onurays
ce028f8bd2 Gathering reason input is refactored. 2020-02-11 00:10:24 +03:00
Benoit Marty
a7274b9df0 Version++ 2020-02-10 21:41:59 +01:00
Benoit Marty
c498416075 Merge branch 'release/0.15.0' 2020-02-10 21:40:36 +01:00
Benoit Marty
a9d6cb7be3 Merge branch 'release/0.15.0' into develop 2020-02-10 21:40:35 +01:00
Benoit Marty
d71797319c Prepare release 0.15.0 2020-02-10 21:40:27 +01:00
onurays
ba0133a047 Reason input for redacting event is added. 2020-02-10 23:03:37 +03:00
Benoit Marty
01a7ea0bd7 Merge branch 'feature/fix_notif' into develop 2020-02-10 19:16:20 +01:00
Benoit Marty
2e7fa23ce7 Fix #980 2020-02-10 19:13:35 +01:00
Benoit Marty
6750237764 Merge pull request #976 from vector-im/feature/viewEvents
Use View events
2020-02-10 16:46:21 +01:00
Benoit Marty
588a644e02 Merge pull request #975 from vector-im/feature/cleanup
Cleanup push rule code
2020-02-10 16:32:24 +01:00
onurays
1bc1c8ec4a Reorder improvements. 2020-02-10 18:17:22 +03:00
Benoit Marty
7e362be568 Convert to ViewEvents -> Cleanup after review 2020-02-10 16:10:57 +01:00
onurays
83d83e0812 Show confirmation dialog before deleting a message
Fixes #967
2020-02-10 17:58:48 +03:00
Benoit Marty
c97d298166 Merge pull request #948 from vector-im/feature/artifacts_downloading
Feature/artifacts downloading
2020-02-10 14:52:25 +01:00
Benoit Marty
81413e49bc Remove '(yet)' 2020-02-10 14:51:35 +01:00
Benoit Marty
b24f133105 Import strings from Riot 2020-02-10 14:50:21 +01:00
Benoit Marty
5b13be6332 Rename 2020-02-07 19:58:14 +01:00
Benoit Marty
abeb741cad Convert to ViewEvents -> RoomDetailViewModel part 5 2020-02-07 19:50:15 +01:00
Benoit Marty
84e1169525 Convert to ViewEvents -> RoomDetailViewModel part 4 2020-02-07 19:45:54 +01:00
Benoit Marty
27e217fce5 Convert to ViewEvents -> RoomDetailViewModel part 3 2020-02-07 19:41:23 +01:00
Benoit Marty
bdb1f850b2 Convert to ViewEvents -> RoomDetailViewModel part 2 2020-02-07 19:37:30 +01:00
Benoit Marty
0338535efa Convert to ViewEvents -> RoomDetailViewModel part 1 2020-02-07 19:27:18 +01:00
Benoit Marty
0dd3894a49 Use handle() pattern 2020-02-07 19:20:02 +01:00
Benoit Marty
c8ff8d3c9e Convert to ViewEvents -> DeviceListBottomSheetViewModel 2020-02-07 18:59:31 +01:00
Benoit Marty
70973c3302 Convert to ViewEvents -> CrossSigningSettingsViewModel 2020-02-07 18:47:54 +01:00
Benoit Marty
a930313bf3 Convert to ViewEvents -> VerificationBottomSheetViewModel 2020-02-07 18:27:13 +01:00
Benoit Marty
35054dc5e8 Convert to ViewEvents -> CreateDirectRoomViewModel 2020-02-07 18:11:53 +01:00
Benoit Marty
33b123f719 Convert to ViewEvents -> GroupListViewModel 2020-02-07 18:02:42 +01:00
Benoit Marty
24667f38b8 Convert to ViewEvents -> RoomMemberProfileViewModel 2020-02-07 17:47:37 +01:00
Benoit Marty
256a6e4322 Convert to ViewEvents -> RoomDetailViewModel 2020-02-07 17:34:26 +01:00
Benoit Marty
7f5cc77ee0 Convert to ViewEvents -> DevicesViewModel 2020-02-07 17:14:03 +01:00
Benoit Marty
c34307ecf7 Convert to ViewEvents -> RoomDirectoryViewModel 2020-02-07 16:32:01 +01:00
Benoit Marty
dd13b6bd99 Add test for DisplayName condition (passing) 2020-02-07 15:44:46 +01:00
Benoit Marty
9df699db59 Reorder tests 2020-02-07 15:35:09 +01:00
Benoit Marty
8bdb2b88fd make the test compile 2020-02-07 15:32:36 +01:00
Benoit Marty
2a534b5874 Handle SenderNotificationPermissionCondition 2020-02-07 15:32:20 +01:00
Benoit Marty
f719da96ed Rename Condition.Kind enum values and add some documentation 2020-02-07 15:02:39 +01:00
Benoit Marty
31e5c0eb1a Improve algorithm 2020-02-07 14:48:08 +01:00
Benoit Marty
34c5f37bbc findAll() does not return null value 2020-02-07 14:30:44 +01:00
Benoit Marty
9aadbbc3c7 Rework DefaultConditionResolver, and create RoomGetter 2020-02-07 14:27:24 +01:00
Benoit Marty
57758af2d7 Merge pull request #974 from vector-im/feature/crosssigning_cleanup
Improve prompt password dialog
2020-02-07 14:02:15 +01:00
Benoit Marty
0a2474ef12 ktlint 2020-02-07 14:01:18 +01:00
Valere
67bc100782 Merge pull request #959 from vector-im/feature/roomshields_perf
Refactor Room Shield / Profile shield
2020-02-06 16:54:17 +01:00
Benoit Marty
5fedfd9286 Cleanup 2020-02-06 16:00:05 +01:00
Valere
1917fbcc93 eventBus private 2020-02-06 13:56:57 +01:00
Valere
a48bf61ad7 quick dispatch to correct thread (will need more work) 2020-02-06 13:56:57 +01:00
Valere
5c1fcc47a1 Catch all decryption fails in mapping 2020-02-06 13:56:57 +01:00
Valere
d80c15f52f Dispatch init on crypto thread to avoid blocking cold start 2020-02-06 13:56:57 +01:00
Valere
3e2219cbb5 Ignore interrupted exception in setupRx 2020-02-06 13:56:57 +01:00
Valere
bf2e01b8c3 More Rx startWithcallable 2020-02-06 13:56:57 +01:00
Valere
75131fdf44 Post merge fix 2020-02-06 13:56:57 +01:00
Valere
911ff8cf16 cleaning (klint) 2020-02-06 13:56:57 +01:00
Valere
4506b7d6e6 Fix / annoying loading to get active sessions
Start with what's known locally
2020-02-06 13:56:57 +01:00
Valere
320dc4accd Refactor Room Shield / Profile shield 2020-02-06 13:56:57 +01:00
Benoit Marty
ffc9a595dd Merge pull request #964 from vector-im/feature/firefox_account_sso
Support SSO login with Firefox account (#606)
2020-02-06 11:28:16 +01:00
Benoit Marty
1602c6544f We have too many strings... 2020-02-05 18:06:39 +01:00
Benoit Marty
79242c8c16 Merge pull request #892 from vector-im/feature/indonesian_check
Import Indonesian resource check from TravisCI to Weblate pipeline
2020-02-05 18:02:00 +01:00
Benoit Marty
7d00aabc85 Share the same Copyright header. Disabled for XML files 2020-02-05 17:48:59 +01:00
Benoit Marty
ef72f2246a Import Indonesian resource check from TravisCI to Weblate pipeline 2020-02-05 17:47:11 +01:00
Benoit Marty
51c2b9e1e9 Import strings from Riot 2020-02-05 17:35:23 +01:00
Benoit Marty
2a4c8b3199 Support SSO login with Firefox account (#606) 2020-02-05 15:21:54 +01:00
Benoit Marty
3189c114dc Merge pull request #945 from vector-im/feature/stabilization_2
Feature/stabilization 2
2020-02-05 15:19:16 +01:00
Benoit Marty
50814dafe9 Update wording 2020-02-05 14:19:04 +01:00
Benoit Marty
dcd7d17ffd Fix compilation issue after merge and update CHANGES.md 2020-02-05 14:13:43 +01:00
Benoit Marty
d9c007d017 Merge branch 'develop' into feature/stabilization_2 2020-02-05 12:57:42 +01:00
Benoit Marty
05dd587fa8 Move FORMAT_MATRIX_HTML to a proper object and so fix a wrong usage issue 2020-02-05 12:44:31 +01:00
Benoit Marty
3384d91adb Rename MessageContent.type to MessageContent.msgType for code clarity and update a few the Javadoc 2020-02-05 12:39:26 +01:00
Benoit Marty
f72e5c1d94 Explain why and when to use EllipsizingTextView 2020-02-05 12:16:43 +01:00
Benoit Marty
a3ec0e03a0 Use NoOpMatrixCallback when it's possible 2020-02-05 12:05:12 +01:00
Benoit Marty
47ee2a24a7 Move NoOpMatrixCallback to MatrixCallback.kt file 2020-02-05 11:58:08 +01:00
Benoit Marty
b8096f21ea Restore Copyright and cleanup 2020-02-05 11:56:07 +01:00
Benoit Marty
a17ec14dd7 Cleanup and little change on Throwable logging 2020-02-05 11:39:23 +01:00
Benoit Marty
062c4559a2 Merge pull request #955 from vector-im/feature/room_history_in_e2e
Feature/room history in e2e
2020-02-04 21:18:26 +01:00
Benoit Marty
f7d511df38 Ganfra's review 2020-02-04 17:48:17 +01:00
Benoit Marty
6bff951c72 Keep MXCryptoConfig, but do a correct usage of it 2020-02-04 15:37:46 +01:00
Benoit Marty
bd033866a8 Encrypt for invited users by default, if the room state allows it (#803) 2020-02-04 14:41:32 +01:00
Benoit Marty
3e9b2e4a06 Use the correct enableEncryption() method 2020-02-04 14:04:48 +01:00
Ganard
ed9c3379bf Rename file 2020-02-04 13:50:10 +01:00
Ganard
1728d31401 Fix some issues and make test passes 2020-02-04 13:19:02 +01:00
Benoit Marty
225a6e00e6 Add doc and reorder 2020-02-04 12:10:51 +01:00
Benoit Marty
96a729ab2b Move file 2020-02-04 11:53:09 +01:00
Benoit Marty
badb4042ce Add sign_apk_unsafe.sh to the repo 2020-02-04 11:53:09 +01:00
Benoit Marty
c3bb421c1e ignore .idea/inspectionProfiles 2020-02-04 11:53:09 +01:00
Benoit Marty
09212a05d0 Add a script to download artifacts from buildkite 2020-02-04 11:53:09 +01:00
Benoit Marty
e02430bcd0 Ask for permission before opening the camera (#934) 2020-02-03 20:06:37 +01:00
Benoit Marty
4335fa4f72 ktlint 2020-02-03 16:26:18 +01:00
Benoit Marty
6df5edbad1 Version++ 2020-02-03 16:20:55 +01:00
Benoit Marty
007fbf8ed3 Merge branch 'release/0.14.3' 2020-02-03 16:17:55 +01:00
Benoit Marty
362799ac08 Merge branch 'release/0.14.3' into develop 2020-02-03 16:15:52 +01:00
Benoit Marty
0049af7980 Prepare version 0.14.3 2020-02-03 16:15:38 +01:00
Ganard
f454078c6b Clean code 2020-02-03 16:14:36 +01:00
Benoit Marty
783a514496 Improve prompt password dialog
Reveal password, inline error when empty
2020-02-03 15:53:44 +01:00
Ganard
88755a79b4 In memory sending: fix broken filtering 2020-02-03 15:18:19 +01:00
Ganard
e6cd8a3a86 Merge develop into feature/stabilization_2 2020-02-03 13:59:20 +01:00
Valere
ce13e824b6 Merge remote-tracking branch 'origin/develop' into develop 2020-02-02 15:28:05 +01:00
Valere
b182a63ea1 fix concurrent co mofification 2020-02-02 15:28:00 +01:00
Benoit Marty
9ced2048d2 Version++ 2020-02-02 14:26:09 +01:00
Benoit Marty
cfee2f93f2 Prepare v0.14.2 2020-02-02 14:06:21 +01:00
Benoit Marty
97aca28c0d Merge branch 'release/0.14.2' 2020-02-02 14:05:50 +01:00
Benoit Marty
b158729b53 Prepare v0.14.2 2020-02-02 14:05:12 +01:00
Valere
367057cc29 Fix / cold start 2020-02-02 14:01:45 +01:00
Benoit Marty
5fb4f274f9 Version ++ 2020-02-02 08:00:57 +01:00
Benoit Marty
a35302eae0 Merge branch 'release/0.14.1' 2020-02-02 07:56:00 +01:00
Benoit Marty
6f60f1c6b4 Merge branch 'release/0.14.1' into develop 2020-02-02 07:56:00 +01:00
Benoit Marty
435d8cbc55 Prepare v0.14.1 2020-02-02 07:55:46 +01:00
Benoit Marty
bb0fafcb2f Merge pull request #929 from vector-im/feature/xsigning_fix_4
Feature/xsigning fix 4
2020-02-02 03:13:54 +01:00
Benoit Marty
1d2928b3fb ktlint 2020-02-02 03:13:15 +01:00
Benoit Marty
40b0f60964 Fix issue in dark theme. Also do not limit subtitles to 2 lines 2020-02-02 03:11:27 +01:00
Valere
96a556f449 Fix / Race causing key requests to be sent to early in xsigning 2020-02-02 01:21:08 +01:00
Valere
d436d3b8d4 Move rx logs to verbose 2020-02-02 00:41:36 +01:00
Valere
845f3f5ad1 FIx / use password textedit 2020-02-02 00:04:15 +01:00
Valere
fb838e5407 Fixes #813 2020-02-01 23:24:05 +01:00
Benoit Marty
40c70f64a8 Merge branch 'feature/to_device_done' into develop 2020-02-01 18:48:34 +01:00
Valere
245b3717b9 Send done in toDevice 2020-02-01 18:25:31 +01:00
Benoit Marty
9e15891053 Version++ 2020-02-01 17:21:41 +01:00
Benoit Marty
637eba277f Merge branch 'release/0.14.0' 2020-02-01 17:20:05 +01:00
Benoit Marty
649ebf4a34 Merge branch 'release/0.14.0' into develop 2020-02-01 17:20:04 +01:00
Benoit Marty
e9f220cca2 Prepare release 0.14.0 2020-02-01 17:19:53 +01:00
Benoit Marty
8a9bd97a88 Merge pull request #924 from vector-im/feature/crossing_fix_2
Feature/crossing fix 2
2020-02-01 17:17:30 +01:00
Valere
26400e6372 Prompt before resetting keys 2020-02-01 15:20:04 +01:00
Valere
282be29247 temp shield for read only 2020-02-01 15:20:04 +01:00
Benoit Marty
6ee7ee1460 Fix blink effect 2020-02-01 15:04:22 +01:00
Valere
2e0a84ccc9 display profile faster from known info 2020-02-01 15:03:00 +01:00
Valere
9d6e7d7bd0 Learn more shows my devices when keys not trusted 2020-02-01 14:36:17 +01:00
Valere
8f7b18239d Warn on verify when private keys not known 2020-02-01 14:36:17 +01:00
Benoit Marty
10094a212c ktlint 2020-02-01 14:02:51 +01:00
Benoit Marty
53e36dca9c ZXing 3.3.3 because of https://github.com/zxing/zxing/issues/1170 2020-02-01 13:52:33 +01:00
Benoit Marty
6ec2dd8c00 Add startWith and logs 2020-02-01 12:12:40 +01:00
Benoit Marty
f098d6cf5b Add startWith 2020-02-01 11:45:45 +01:00
Benoit Marty
cd606ba8a1 RoomMember decoration 2020-02-01 11:37:16 +01:00
Benoit Marty
59abee10f8 Convert to ConstraintLayout 2020-02-01 11:37:16 +01:00
Valere
64df9e23c2 Room Profile / Just show e2e status remove learn more 2020-02-01 11:03:00 +01:00
Valere
fc4f5faffd Update Room decoration algo 2020-02-01 10:21:29 +01:00
Benoit Marty
256f8b77aa Add Timber to rx module 2020-02-01 10:09:22 +01:00
Benoit Marty
f2f775cb99 Add TODOs 2020-02-01 01:17:18 +01:00
Benoit Marty
2616a889ef Merge pull request #923 from vector-im/feature/xcrossing_fix
Decoration in room profile and improve Rx flow
2020-01-31 20:48:20 +01:00
Benoit Marty
ccd4c1ed86 ktlint 2020-01-31 20:46:33 +01:00
Benoit Marty
bb92882958 (partially) Fix glitch 2020-01-31 20:28:57 +01:00
Benoit Marty
46dd17644f Room decoration - in room profile UI 2020-01-31 20:12:02 +01:00
Benoit Marty
7e34b2a672 Room decoration - convert to ConstraintLayout 2020-01-31 20:02:01 +01:00
Benoit Marty
c3c88c387b Improve Rx chain and cleanup 2020-01-31 19:55:22 +01:00
Valere
51e0f945a7 Quick Room Decoration 2020-01-31 18:52:33 +01:00
Ganard
37230b0614 Fix issues with read marker and jumpToBottom 2020-01-31 18:09:34 +01:00
Benoit Marty
fd3619b100 Merge pull request #770 from vector-im/cross_signing
Cross signing
2020-01-31 17:05:18 +01:00
Benoit Marty
e18b9d5155 Room decoration - UI in room list 2020-01-31 16:10:52 +01:00
Ganard
ec6d78bf96 Merge branch 'develop' into feature/stabilization_2 2020-01-31 15:47:33 +01:00
Ganard
759b680e63 Timeline/Sync: Fix some issues 2020-01-31 15:28:35 +01:00
Benoit Marty
f5ecf4bd90 Room decoration - UI 2020-01-31 15:02:54 +01:00
Valere
27c74c9118 live device in settings 2020-01-31 14:42:00 +01:00
Benoit Marty
4d91bc934b Fix ktlint 2020-01-31 14:12:03 +01:00
Valere
5c547794f2 Merge branch 'develop' into cross_signing 2020-01-31 14:09:40 +01:00
Benoit Marty
43358cd86c Make self verification work! 2020-01-31 12:18:27 +01:00
Benoit Marty
87b76d10dd Format 2020-01-31 11:11:27 +01:00
Benoit Marty
80f4f95f81 QRCode: requestId is not supposed to be an eventId 2020-01-31 11:11:27 +01:00
Valere
8e5c7239cf Settings Sessions / Now live + support devices with no keys 2020-01-31 10:33:53 +01:00
Valere
7b385c3b36 Quick copy change device -> session 2020-01-31 10:00:44 +01:00
Valere
4fb59aadb1 Fix / ensure RoomKeyRequest are made after device is verified 2020-01-31 09:39:31 +01:00
Valere
850c830e1f Fix / auto ready request when waiting bottomsheet on screen 2020-01-31 09:16:03 +01:00
Benoit Marty
9dde43f65b PR Review: var -> val, internal and other cleanup 2020-01-30 23:40:25 +01:00
Benoit Marty
9d566e9352 Merge pull request #918 from vector-im/feature/sort_room_members
Sort room members by display names
2020-01-30 21:06:40 +01:00
Benoit Marty
eb46a4949f Merge pull request #920 from duncanturk/patch-1
Add "get it on F-Droid"
2020-01-30 21:05:53 +01:00
Benoit Marty
506c2dd262 Merge pull request #921 from vector-im/decorations
Add field in QR code and make some optional
2020-01-30 18:48:30 +01:00
Benoit Marty
ccd857016c ktlint 2020-01-30 18:46:08 +01:00
Benoit Marty
225e4e0433 To Device Verification Request 2020-01-30 18:43:50 +01:00
Ganard
5e1b59f9d3 Timeline: handle an in memory local echo to make the UI snappier 2020-01-30 17:13:44 +01:00
Benoit Marty
069bd3c258 use isMe 2020-01-30 16:59:22 +01:00
Benoit Marty
fb98d6ef42 QRCode: add other_device_key field and make it optional, along with other_user_key 2020-01-30 16:46:12 +01:00
Benoit Marty
6282f81bc4 Remove typo 2020-01-30 16:46:12 +01:00
Valere
a5ca2b1d34 Fix / incoming start verif popup should not show when bottomsheet there 2020-01-30 16:36:13 +01:00
Valere
50d5ad3625 Self verification + toDevice Request 2020-01-30 16:35:42 +01:00
Valere
03c5e61b2e Fix / post merge 2020-01-30 16:35:06 +01:00
Valere
4ddd831d7f Prepare support for toDevice .request 2020-01-30 16:11:34 +01:00
Valere
ff95392e10 Fix / Refresh trust state on own keys/device trust change 2020-01-30 16:10:59 +01:00
Benoit Marty
e2c2c2418c Merge pull request #919 from vector-im/qr_step_validate
Qr step validate
2020-01-30 14:14:16 +01:00
Christopher Rossbach
e44dc347c6 Add "get it on F-Droid"
RiotX is on F-Droid now
2020-01-30 12:07:33 +01:00
Benoit Marty
fbd0bbc575 Improve clarity of the algorithm to enable encryption for DMs 2020-01-30 11:24:05 +01:00
ganfra
b848d0530f Update realm to 6.1.0: should fix some of the native crashes 2020-01-30 11:04:37 +01:00
Benoit Marty
2bccd19f84 QRcode: Url encode the keys 2020-01-30 10:17:04 +01:00
Benoit Marty
2111daea52 Add a step to confirm that other user has scanned the SR code 2020-01-30 10:09:59 +01:00
ganfra
9fc3fa7f19 Update some libs and remove incremental from dagger 2020-01-29 21:14:38 +01:00
Benoit Marty
57a13fa30d Sort room members by display names 2020-01-29 18:07:57 +01:00
Benoit Marty
c4649a5824 Merge pull request #916 from vector-im/debug_qr
Negotiate E2E by default for DMs (#907)
2020-01-29 18:02:43 +01:00
ganfra
71a02a58af Sync/Timeline: handle displayName isUnique 2020-01-29 17:30:31 +01:00
Benoit Marty
6f6c3184dd Avoid test if previous result is null 2020-01-29 17:13:41 +01:00
Benoit Marty
d7feb6dd5c Merge pull request #913 from vector-im/feature/rainbow
Rainbow
2020-01-29 16:35:34 +01:00
Benoit Marty
e6c3f7c77b Nicer API 2020-01-29 16:26:19 +01:00
Benoit Marty
8b6ffc2fb1 ktlint 2020-01-29 16:18:33 +01:00
Benoit Marty
ae36846aaf Negotiate E2E by default for DMs (#907) 2020-01-29 16:11:23 +01:00
Benoit Marty
237da5bb16 No need to have mutable list in param 2020-01-29 16:03:28 +01:00
ganfra
7f72af426b Timeline: fix getContext 2020-01-29 16:02:53 +01:00
Benoit Marty
a4abe5f552 Set timeout to 60s when creating a room 2020-01-29 16:00:07 +01:00
Benoit Marty
e1ddde5501 Make CreateRoomParams a regular data class 2020-01-29 14:23:32 +01:00
Valere
754ca3c582 Fix / fail to update usk when dl own keys 2020-01-29 14:00:02 +01:00
Benoit Marty
70b04dbaea Disable not passing test, to avoid waiting too long when running the test suite 2020-01-29 12:36:38 +01:00
Benoit Marty
b44b6726ed Also update the tests 2020-01-29 12:29:19 +01:00
Benoit Marty
80ec199135 Convert VerificationTxState to a sealed class 2020-01-29 12:00:49 +01:00
Benoit Marty
305dfd4c7a Merge pull request #911 from vector-im/feature/encryption_notice
Modify encryption notice
2020-01-29 10:32:23 +01:00
Benoit Marty
73bb4bb785 Merge pull request #912 from vector-im/feature/e2e_opt_out
e2e opt in when creating room (not DM)
2020-01-29 10:29:33 +01:00
Valere
1d84ccd64a Merge pull request #881 from vector-im/xsigning_sdk
Xsigning sdk
2020-01-29 10:26:46 +01:00
Valere
63e36b0403 Remove unused test 2020-01-29 10:16:57 +01:00
Valere
2c568b4de9 clean klint 2020-01-29 09:59:09 +01:00
Benoit Marty
27fe4e3680 Fix build and add tests 2020-01-29 09:44:53 +01:00
Benoit Marty
007b0cabf2 Add a few TUs 2020-01-28 22:43:10 +01:00
Benoit Marty
b2338dfcd3 Make the TU passes 2020-01-28 22:35:40 +01:00
Benoit Marty
6d7d4993a6 Add TUs for RainbowGenerator (not all passing) 2020-01-28 21:56:02 +01:00
Benoit Marty
da9b9f4864 Make the whole cell clickable 2020-01-28 21:31:02 +01:00
Benoit Marty
ef0b438a89 Give the possibility to enable encryption when creating room (#837) 2020-01-28 21:31:02 +01:00
Ganard
a8f783bbfa Add state events to chunks 2020-01-28 18:59:21 +01:00
Valere
9a79297e14 Merge pull request #910 from vector-im/qr_code_step_2
Qr code step 2
2020-01-28 18:11:09 +01:00
Valere
a57393cafa More log + quick fix in settings 2020-01-28 18:09:17 +01:00
Benoit Marty
e12de3fba0 Merge pull request #891 from vector-im/feature/event-unknown
Feature/event unknown
2020-01-28 17:30:02 +01:00
Benoit Marty
2eeeea3377 Encryption is enabled only for MEGOLM. 2020-01-28 17:19:22 +01:00
Benoit Marty
976a8fc568 Hide the algorithm when turning on e2e (#897) 2020-01-28 16:36:28 +01:00
Benoit Marty
b7ecfd997d Fix compilation issue after rebase 2020-01-28 16:02:20 +01:00
Benoit Marty
e0b3ea7e48 QrCode: WIP 2020-01-28 15:55:44 +01:00
Benoit Marty
9c829e62e6 QrCode: WIP 2020-01-28 15:55:44 +01:00
Benoit Marty
20c7e4c3ad QrCode: improve UI 2020-01-28 15:55:44 +01:00
Benoit Marty
69ab5e43d5 QrCode: WIP 2020-01-28 15:55:02 +01:00
Benoit Marty
f46023e84c QrCode: WIP 2020-01-28 15:55:02 +01:00
Benoit Marty
d8d465f70b QrCode: WIP 2020-01-28 15:53:57 +01:00
Benoit Marty
8659216955 QrCode: WIP 2020-01-28 15:53:57 +01:00
Benoit Marty
39e746413a QrCode: WIP 2020-01-28 15:53:57 +01:00
Benoit Marty
0aaba26f17 Rename classes 2020-01-28 15:53:57 +01:00
Benoit Marty
fc04833157 Rename parameter 2020-01-28 15:53:57 +01:00
Benoit Marty
f80861bed8 Add TODO 2020-01-28 15:53:57 +01:00
Benoit Marty
9e796067cc Do not support SHOW or SCAN if cross-signing is not enabled 2020-01-28 15:53:57 +01:00
Benoit Marty
fb5148fd43 Avoid to inject credential (again) 2020-01-28 15:52:17 +01:00
Benoit Marty
be77017209 Avoid injecting credentials. Inject userId and deviceId instead
And cleanup API
2020-01-28 15:48:09 +01:00
Benoit Marty
962b85b041 Add TODO 2020-01-28 15:48:09 +01:00
Benoit Marty
adc2d570eb QR code: handle the case where other user can scan QR codes 2020-01-28 15:48:09 +01:00
Benoit Marty
df49ab8362 QR code: update code which build URL 2020-01-28 15:48:09 +01:00
Benoit Marty
efc8cfb9a1 QR code: modify APIs 2020-01-28 15:48:09 +01:00
Benoit Marty
345824daa2 Keep on renaming 2020-01-28 15:48:09 +01:00
Benoit Marty
050eb0af9d Create dedicated View and Epoxy item for QrCode 2020-01-28 15:48:09 +01:00
Valere
ca4ed6e1bd Fix / Error management and clear keys 2020-01-28 15:35:11 +01:00
Ganard
bf7c53ecab Sync/pagination: get a working version 2020-01-28 14:46:26 +01:00
Valere
f021f8110d post merge 2020-01-28 12:04:19 +01:00
Valere
109ff4f908 Merge branch 'cross_signing' into xsigning_sdk 2020-01-28 11:33:54 +01:00
Valere
c9f0209ebf post merge fix 2020-01-28 11:23:37 +01:00
Valere
7daa088618 Merge branch 'develop' into cross_signing 2020-01-28 11:13:31 +01:00
Valere
83e44ac96e Fix / cross signing info live data not always updated 2020-01-28 11:02:12 +01:00
Ganard
15b0bea870 Use clone for retrofit request to be able to retry 2020-01-28 10:13:36 +01:00
Ganard
e5e62dc4a7 Fix SyncService and Alarm 2020-01-28 10:13:19 +01:00
Benoit Marty
f3e88c75cf Fix typo 2020-01-28 09:42:56 +01:00
Valere
ea6e8a6789 Basic debug screen to setup keys 2020-01-27 23:51:08 +01:00
Valere
6cece03998 Profile detailed device info + verify manually 2020-01-27 17:55:00 +01:00
Ganard
f3db43f317 Ellipsize: introduce EllipsizingTextView 2020-01-27 12:41:43 +01:00
Valere
08ae0b485a Profile Screen / Add show device list trust screen 2020-01-27 09:25:58 +01:00
Valere
665c577747 SDK / update trust on key change + live method in Service 2020-01-27 09:25:16 +01:00
ganfra
3a89a30056 Continue reworking sync/timeline events handling 2020-01-25 18:59:45 +01:00
Valere
d60351bcb7 Verify from RoomMember Profile 2020-01-24 19:15:23 +01:00
Ganard
1d8b81bb04 Try reworking events/timeline process [WIP] 2020-01-24 18:43:35 +01:00
Valere
a758efc018 Renamed room transport classes 2020-01-24 11:32:24 +01:00
Valere
d0addc4c4f Refactored Verification Classes 2020-01-24 11:29:26 +01:00
Valere
bb5179140c Update profile screen for xSigning 2020-01-24 09:14:32 +01:00
Benoit Marty
e9ea69f055 Add support for /rainbow and /rainbowme command (#879) 2020-01-23 23:34:21 +01:00
ganfra
7bcae75314 Remove some code from main thread 2020-01-23 20:31:18 +01:00
ganfra
4331d2ef47 Network: reword the strategy for handling NetworkConnectivity (remove Merlin) 2020-01-23 19:08:55 +01:00
Valere
e47791f290 Merge pull request #888 from vector-im/qr_code
Qr code
2020-01-23 16:41:23 +01:00
Valere
91ae96a153 QuickFix / Do not verify yourself in dm 2020-01-23 16:16:57 +01:00
Valere
0148949a4f Fix / prevent verification toaster to show when in good room 2020-01-23 16:13:46 +01:00
Valere
65cb812fc6 Fix / Unknown transaction when started by other after request 2020-01-23 16:04:29 +01:00
Valere
e8a4f1fb90 Fix: .cancel won't appear in debug show all 2020-01-23 16:04:03 +01:00
Benoit Marty
632832a651 Nearly same code for DefaultItem and NoticeItem 2020-01-23 15:44:41 +01:00
Benoit Marty
d530c64a84 Render defaultItem as other item: display user avatar
Also ensure bottom sheet always has a header, for user avatar and date
2020-01-23 15:35:46 +01:00
Benoit Marty
426e291ce9 i18n for RiotX limitation messages 2020-01-23 14:46:36 +01:00
Valere
1276d1f39d Update My device list + action to verify 2020-01-23 13:57:17 +01:00
Benoit Marty
4a1012cf81 Add TODOs 2020-01-23 11:48:08 +01:00
Benoit Marty
5819790c1b Distinguish Show SR code and Scan QR code capability 2020-01-23 11:25:44 +01:00
Benoit Marty
b3089343ad Support SCAN method (WIP) 2020-01-23 10:47:29 +01:00
ganfra
c65f25d7ae Rx: fix startWith on mainThread 2020-01-23 10:18:22 +01:00
Benoit Marty
37b950897f Base64 no wrap and extension for the reverse operation 2020-01-23 10:17:07 +01:00
ganfra
fee2ec6b66 Scroll when event build come from sync/send + remove use of monarchy writeAsync 2020-01-22 20:33:52 +01:00
Benoit Marty
d2fab91e9d Improve code 2020-01-22 19:08:21 +01:00
Benoit Marty
c323b61575 Ignore typo 2020-01-22 18:27:59 +01:00
Benoit Marty
0e55f81879 Ensure all is escaped properly 2020-01-22 18:26:34 +01:00
Benoit Marty
cbf418c401 Update after MSC change 2020-01-22 18:22:01 +01:00
Benoit Marty
41c691f26c Create QrCodeData class and method to convert to URL and vice versa, with TUs 2020-01-22 17:58:25 +01:00
Benoit Marty
81337d1624 Also keep the same parameter order: (userId, deviceId) to avoid silly errors 2020-01-22 17:00:16 +01:00
Benoit Marty
79df6b8402 Start plugin QR code to the code 2020-01-22 15:56:43 +01:00
Benoit Marty
537b1be0c5 Update wording 2020-01-22 15:26:26 +01:00
Benoit Marty
c1259161e5 QRCode: generate and scan QRCodes 2020-01-22 15:03:56 +01:00
ganfra
76065ac4fc Read: allow setting read marker and read receipt to latest known event independently 2020-01-22 14:43:39 +01:00
ganfra
d93050240a Start reworking networkConnectivityCheck (WIP) 2020-01-22 14:41:42 +01:00
Benoit Marty
75e39535bc Merge pull request #883 from vector-im/feature/share
Improve the room list when sharing to RiotX
2020-01-22 11:49:50 +01:00
Benoit Marty
c971f18fc0 Add section header when displaying room list to share (#771) 2020-01-22 11:49:30 +01:00
Benoit Marty
3c2fa40b58 Sharing things to RiotX: sort list by recent room first (#771) 2020-01-22 11:49:04 +01:00
Benoit Marty
0b74863c6d Merge pull request #877 from vector-im/feature/version_name
F-Droid: fix the "-dev" issue in version name (#815)
2020-01-22 11:45:36 +01:00
Benoit Marty
dca950140d Merge branch 'develop' into feature/version_name 2020-01-22 11:45:27 +01:00
Benoit Marty
a13a78ccc0 Merge pull request #874 from vector-im/feature/authors
Builds repoductibility and Authors file
2020-01-22 11:43:51 +01:00
Benoit Marty
7327dc97b4 Merge pull request #876 from vector-im/feature/room_settings
Room settings, and enable encryption in unencrypted rooms (#212)
2020-01-22 11:40:26 +01:00
ganfra
2bddf61afe Update realm to 6.1.0: should fix some of the native crashes 2020-01-21 15:15:29 +01:00
Benoit Marty
da3e547d82 Create facility extension to observe ViewEvents 2020-01-21 15:08:49 +01:00
Valere
a0aa1f34d3 Quick Fix todevice verif broken
Added dbg screen for cross signing
2020-01-21 14:58:06 +01:00
ganfra
d1b8d81fb1 Fix double read receipts 2020-01-21 14:17:04 +01:00
Benoit Marty
e81c804ed6 Ensure when will be exhaustive 2020-01-21 12:51:28 +01:00
Benoit Marty
be371f9279 Introduce ViewEvents in ViewModel and code harmonization 2020-01-21 12:51:28 +01:00
Valere
a6364f0be5 remove dead code 2020-01-21 10:25:57 +01:00
Valere
390879e3fd Added check self keys + force DL after initialize Xsigning 2020-01-21 10:25:57 +01:00
Valere
6ab540045b Refactoring / deprecation of MXDeviceInfo
introduced TrustLevels
2020-01-21 10:25:57 +01:00
Valere
98ba2d39a8 SAS verif, support signing and verification of Cross Signing 2020-01-21 10:25:57 +01:00
Valere
859c75df98 Initial commit 2020-01-21 10:25:57 +01:00
Valere
ea9166e0c6 Merge pull request #811 from vector-im/verification_toasters
[Cross Signing DM Verif]  Basic Incoming request toast + cleaning
2020-01-21 10:21:24 +01:00
Benoit Marty
d672313649 Room settings: use boolean instead of Async 2020-01-20 17:41:29 +01:00
Benoit Marty
f5e6b4b857 F-Droid: fix the "-dev" issue in version name (#815) 2020-01-20 16:08:57 +01:00
Benoit Marty
28db05e509 Cleanup and copy wording from Riot-Web 2020-01-20 15:39:14 +01:00
Benoit Marty
56b140fcb4 Room settings: rename stuff for genericity 2020-01-20 15:23:40 +01:00
Benoit Marty
eee6969b02 Room settings, and enable encryption in unencrypted rooms (#212) 2020-01-20 15:11:21 +01:00
Benoit Marty
825427b7b0 Add a first content into the AUTHORS file 2020-01-17 16:50:18 +01:00
Benoit Marty
8c32796d5c Ensure builds are reproducible (#842) 2020-01-17 16:06:58 +01:00
Valere
e45c1e6c2a Fix / post merge issues 2020-01-17 15:17:16 +01:00
Benoit Marty
188cd6beff Verification: improve blinking effect (not perfect yet) 2020-01-17 15:05:06 +01:00
Benoit Marty
aaeb54db7c Verification: Introduce VerificationMethod enum 2020-01-17 15:05:06 +01:00
Benoit Marty
92f26bc20a Verification: migrate to Epoxy - Cleanup 2020-01-17 15:05:06 +01:00
Benoit Marty
fc22b7988f Verification: migrate to Epoxy - Add missing icons 2020-01-17 15:05:06 +01:00
Benoit Marty
878bae1c45 Verification: migrate to Epoxy - Choose Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
a8e81d95cf Verification: migrate to Epoxy - Disable item animation 2020-01-17 15:05:06 +01:00
Benoit Marty
cd1665a8e8 Verification: migrate to Epoxy - Conclusion 2020-01-17 15:05:06 +01:00
Benoit Marty
7170471686 Verification: migrate to Epoxy - create sub packages 2020-01-17 15:05:06 +01:00
Benoit Marty
98020404ff Verification: migrate to Epoxy - rename package 2020-01-17 15:05:06 +01:00
Benoit Marty
b2348427bd Verification: migrate to Epoxy - Emoji Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
832df59b58 Verification: migrate to Epoxy - Request Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
32689facc5 Verification: migrate to Epoxy - Request Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
d3071e5816 Rename layout for consistency 2020-01-17 15:04:36 +01:00
Benoit Marty
83a6f564c3 Ensure BottomSheets call ButterKnife unbinder 2020-01-17 15:04:36 +01:00
Benoit Marty
3b420dbb50 typo 2020-01-17 15:04:36 +01:00
Benoit Marty
9fe155bafd postWork now returns the Cancellable 2020-01-17 15:04:36 +01:00
Benoit Marty
3c982866d8 Restore lost Fragment binding... 2020-01-17 15:04:36 +01:00
Benoit Marty
494ad83704 Inject WorkManagerProvider, to avoid injecting the Android context
Also ensure WorkManager uses a distinct tags for each session (for future multi-sessions support)
2020-01-17 15:04:36 +01:00
Benoit Marty
4543658ae0 Extends SessionWorkerParams 2020-01-17 15:04:36 +01:00
Benoit Marty
689fd1ea90 Fix issue with SessionId for the worker
Also rename some variables
2020-01-17 15:04:36 +01:00
Valere
a95410c118 fix rebase 2020-01-17 15:04:36 +01:00
Valere
8749e49e80 Basic Incoming request toast + cleaning 2020-01-17 15:04:36 +01:00
Valere
8400ab6efe Merge branch 'develop' into cross_signing 2020-01-17 14:57:08 +01:00
Benoit Marty
d1699279fe Version++ 2020-01-17 14:25:25 +01:00
Benoit Marty
f471d9cff8 Merge branch 'release/0.13.0' 2020-01-17 14:24:11 +01:00
Benoit Marty
115058124c Merge branch 'release/0.13.0' into develop 2020-01-17 14:24:10 +01:00
Benoit Marty
7604748d3f Prepare release 0.13.0 2020-01-17 14:23:58 +01:00
Benoit Marty
d46a3ab87f Update CHANGES.md 2020-01-17 14:10:47 +01:00
Benoit Marty
b375463aec Merge pull request #854 from vector-im/feature/profiles_improvements
Feature/profiles improvements
2020-01-17 12:22:16 +01:00
Benoit Marty
cfb752c220 Merge pull request #851 from vector-im/feature/code_style
Share the same code formatting rules
2020-01-17 12:21:34 +01:00
Benoit Marty
c59712732a Introduce listener type for Epoxy items 2020-01-17 12:14:24 +01:00
Benoit Marty
626ebd9c63 Merge pull request #850 from vector-im/feature/e2e_attachments_cleanup
Feature/e2e attachments cleanup
2020-01-17 12:12:48 +01:00
Benoit Marty
5148d7ab46 Room profile: open it also when clicking on the room avatar 2020-01-17 10:15:04 +01:00
Benoit Marty
8c2a55a5a2 Settings: restore "Show read receipts" setting and create categories of per Riot-Web 2020-01-17 10:10:46 +01:00
Benoit Marty
34b6dd4270 Room profile: open room member profile from Event bottom sheet 2020-01-16 21:48:10 +01:00
Benoit Marty
79a68a36bd Room profile: ensure RoomMemberSummaries order is guaranteed 2020-01-16 21:29:05 +01:00
Benoit Marty
78408fa0ec Room profile: create extension join for Collections 2020-01-16 21:18:14 +01:00
Benoit Marty
b56ee9a377 Room profile: fix issue with divider color 2020-01-16 20:53:38 +01:00
Benoit Marty
9eff459ed6 Room profile: remove an extra divider 2020-01-16 16:43:37 +01:00
Benoit Marty
cd44b60bd5 Room profile: make link in room topic clickable 2020-01-16 16:36:56 +01:00
Benoit Marty
17ed8da57a Room profile: fix issue with icon 2020-01-16 16:27:21 +01:00
Benoit Marty
8ae51a95c5 Add formatting policy in the Contributing guide 2020-01-16 16:02:41 +01:00
Benoit Marty
f80f22fed2 XML formatting rule 2020-01-16 15:58:52 +01:00
Benoit Marty
fa57d0f6d5 160 char max per line 2020-01-16 15:27:00 +01:00
Benoit Marty
1104b5d483 DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION 2020-01-16 14:26:52 +01:00
Benoit Marty
464462022c Kotlin code convention 2020-01-16 14:17:29 +01:00
Benoit Marty
1c63d3f8f0 Add rule 2020-01-16 14:15:50 +01:00
Benoit Marty
a916bd4ba8 Share the same code formatting rules 2020-01-16 14:05:14 +01:00
Benoit Marty
15e2c9f3a3 Fix formatting 2020-01-16 12:55:55 +01:00
Benoit Marty
f1a2fb51f5 Properly configure Snackbar styles 2020-01-16 12:51:33 +01:00
Benoit Marty
b0aa9fbd8f Inform user when the download of a file starts 2020-01-16 12:20:39 +01:00
Benoit Marty
d72f1ac576 Avoid exposing internal classes 2020-01-16 11:36:53 +01:00
Benoit Marty
ca157c7567 Better logs 2020-01-16 10:57:08 +01:00
Benoit Marty
ae26bf3369 Signout also clear cache 2020-01-16 10:57:08 +01:00
Benoit Marty
159c96681f Improve attachment encryption and decryption code 2020-01-16 10:57:08 +01:00
Benoit Marty
96d6b75037 Fix broken link 2020-01-16 10:57:08 +01:00
Benoit Marty
72e6181f00 Merge pull request #839 from vector-im/feature/developer
Show all hidden event in the timeline when the developer settings is ON
2020-01-16 10:56:27 +01:00
Benoit Marty
4ae09b8716 Merge pull request #831 from david1hyman/file-decryption-fix
File decryption path was writing incorrect stream to file
2020-01-16 10:50:15 +01:00
ganfra
f128ed437f Merge pull request #843 from vector-im/feature/room_profile
Feature/room profile
2020-01-15 19:17:50 +01:00
ganfra
11c8c8c2bd Fix fdroid background sync 2020-01-15 19:16:50 +01:00
ganfra
2c331671ee Fix string 2020-01-15 17:44:38 +01:00
ganfra
b44ddcfd61 Clean code after Benoit's review #2 2020-01-15 14:57:21 +01:00
ganfra
7c0f2d6e32 Test: fix import 2020-01-15 11:48:02 +01:00
ganfra
52de14b1b5 Clean code after Benoit's review 2020-01-15 11:46:33 +01:00
ganfra
d6e6092eea Profile: add PowerLevelContent for building future actions + fix divider 2020-01-14 19:14:15 +01:00
ganfra
9671a77e5d Clean code and update CHANGES 2020-01-14 18:48:13 +01:00
ganfra
d3415d345f Merge branch 'develop' into feature/room_profile 2020-01-14 18:35:01 +01:00
ganfra
df4df81ef3 Profile: handle ignore/unignore action + adjust UI 2020-01-14 17:08:21 +01:00
Valere
a7c948815c Merge branch 'develop' into cross_signing 2020-01-14 12:31:29 +01:00
Benoit Marty
9d5197b1c8 Show all hidden event in the timeline when the developer settings is ON 2020-01-13 22:47:41 +01:00
ganfra
162f0949fa Profile: Start fetching profile info from a user 2020-01-13 18:44:01 +01:00
ganfra
ae1a24e948 Room member profile: branch the UI and fix some UI issues 2020-01-13 16:49:14 +01:00
Benoit Marty
b5fead18fe Merge pull request #833 from vector-im/feature/typing
Send and render typing events (#564)
2020-01-13 15:17:43 +01:00
Benoit Marty
3c682430d9 Merge pull request #829 from vector-im/feature/stab
Feature/stab
2020-01-13 15:13:07 +01:00
Benoit Marty
717965bc37 Update comment 2020-01-13 15:12:39 +01:00
Benoit Marty
ecc463e920 Cleanup and fix a bug 2020-01-13 10:13:12 +01:00
Benoit Marty
f3e52b96c0 Cleanup 2020-01-11 22:12:56 +01:00
Benoit Marty
dd81fce8d8 Send and render typing events (#564) 2020-01-11 20:50:09 +01:00
Valere
7354eab061 Post merge fixes 2020-01-11 10:16:09 +01:00
David Hyman
1ae58aa6ad DefaultFileService - code was passing the inputStream to the decryption method
but not storing the output of the method anywhere
then it was writing inputStream to file and returning that file handle
changed inputStream to var and used it to store output of decryption method
2020-01-10 14:53:40 -05:00
Valere
fb9abefe59 Merge branch 'develop' into cross_signing 2020-01-10 18:38:54 +01:00
Benoit Marty
095216349e Exception -> Throwable 2020-01-10 18:35:29 +01:00
Benoit Marty
550908fa70 Render events m.room.encryption and m.room.guest_access in the timeline 2020-01-10 18:30:10 +01:00
Benoit Marty
0dbca829ea Fix crash with RoomCreationParams 2020-01-10 18:07:14 +01:00
Benoit Marty
03b5b098c7 Change the way versionCode is computed (#827) 2020-01-10 17:29:34 +01:00
ganfra
171ec4fbdc Room member list: group by power level 2020-01-10 17:03:11 +01:00
Benoit Marty
32d2daee3c Exclude current user from autocompletion with room members 2020-01-10 16:28:14 +01:00
Benoit Marty
de84bb7535 Use more developerMode() 2020-01-10 11:03:42 +01:00
ganfra
81712ae736 RoomProfile: fix some rendering + change anim 2020-01-09 19:47:54 +01:00
ganfra
289951ea4a RoomMemberList : start showing items 2020-01-09 15:54:36 +01:00
Benoit Marty
8c9c65837d Version++ 2020-01-09 15:32:37 +01:00
Benoit Marty
24a7ce7d98 Merge branch 'release/0.12.0' 2020-01-09 15:31:30 +01:00
Benoit Marty
d486e613d8 Merge branch 'release/0.12.0' into develop 2020-01-09 15:31:29 +01:00
Benoit Marty
598d67234c Prepare release 0.12.0 2020-01-09 15:31:13 +01:00
Benoit Marty
fea4f2e129 Merge pull request #816 from vector-im/feature/integration_tests
Start porting of integration tests - Fix issue with multiple sessions
2020-01-09 15:28:29 +01:00
Benoit Marty
687ea1b5b3 ktlint 2020-01-09 15:28:16 +01:00
Benoit Marty
47e3b8ec46 Ensure foreground notification is always displayed 2020-01-09 15:09:37 +01:00
Benoit Marty
dd8c908dc7 Code cleanup 2020-01-09 15:02:39 +01:00
Benoit Marty
9775e8c32b Fix crash in syncService 2020-01-09 15:01:16 +01:00
Benoit Marty
e3205fb493 Fix compilation issue after rebase 2020-01-09 14:26:36 +01:00
Benoit Marty
35f011ba37 Fix ktlint issues 2020-01-09 14:20:17 +01:00
Benoit Marty
ed773dbb96 TI: Introduce doSync method 2020-01-09 14:20:17 +01:00
Benoit Marty
fa821826d2 TI: Import ExportEncryption test (passing) 2020-01-09 14:20:17 +01:00
Benoit Marty
293e3e3ce6 TI: Import AttachmentEncryption test (passing) 2020-01-09 14:20:17 +01:00
Benoit Marty
4244c0e48d TI: Import SAS Test - WIP 2020-01-09 14:20:17 +01:00
Benoit Marty
76e45431da TI: Import keys backup tests 2020-01-09 14:20:17 +01:00
Benoit Marty
f3fb07079e Cleanup tests 2020-01-09 14:20:17 +01:00
Benoit Marty
3ceac70536 Enable encryption on a room, SDK part (#212) 2020-01-09 14:20:17 +01:00
Benoit Marty
0f7209df1f TI: finish the work to identify a session with a sessionId 2020-01-09 14:20:17 +01:00
Benoit Marty
e177251ec0 TI: inject EventBus to allow multiple sessions - WIP 2020-01-09 14:20:17 +01:00
Benoit Marty
6746f68411 TI: create account 2020-01-09 14:20:17 +01:00
Benoit Marty
fc6d845c0d Import tests from legacy SDK 2020-01-09 14:20:17 +01:00
Benoit Marty
93cdce6c3e Cleanup tests 2020-01-09 14:20:17 +01:00
Benoit Marty
ae3381227c Add Unit tests from legacy SDK 2020-01-09 14:20:17 +01:00
Benoit Marty
b6a1ff1ca4 Import string from Riot legacy 2020-01-09 14:17:17 +01:00
Benoit Marty
7ec0227528 Merge pull request #824 from vector-im/feature/wording
Email domain can be limited on some homeserver, i18n of the displayed error (#754)
2020-01-09 12:26:28 +01:00
ganfra
15639b45cf Introduce RoomMemberProfile files 2020-01-09 12:10:49 +01:00
ganfra
f18ec8d021 Merge branch 'develop' into feature/room_profile 2020-01-09 11:56:09 +01:00
Benoit Marty
898bf234da Merge pull request #792 from vector-im/feature/stabilization
Feature/stabilization
2020-01-09 11:43:52 +01:00
Benoit Marty
3d0d95c371 Email domain can be limited on some homeserver, i18n of the displayed error (#754) 2020-01-09 11:34:57 +01:00
ganfra
bd4a595f96 ChunkEntityTest: make it compile again 2020-01-09 11:19:08 +01:00
Benoit Marty
0f7d59a8c7 Cleanup during PR review 2020-01-09 09:42:34 +01:00
Benoit Marty
e14b9b3b20 Fix test compilation issue 2020-01-09 08:03:14 +01:00
Benoit Marty
43c4e20819 Merge pull request #820 from vector-im/feature/developer_mode_crash
Developer mode: Fail-fast (#745)
2020-01-09 07:56:06 +01:00
Benoit Marty
5312b44359 Merge pull request #821 from vector-im/feature/query_latch
Remove CountDownLatch (inspired from #419)
2020-01-09 07:55:38 +01:00
ganfra
8c4d8763a2 Merge branch 'develop' into feature/stabilization 2020-01-08 22:28:08 +01:00
ganfra
383605274c Introduce a very simple query langage and refact autocomplete 2020-01-08 22:17:32 +01:00
Benoit Marty
8032490606 Remove CountDownLatch (inspired from #419) 2020-01-08 18:58:51 +01:00
Benoit Marty
a458997ce0 Merge pull request #818 from vector-im/feature/oss
Exclude play-services-oss-licenses library from F-Droid build (#814)
2020-01-08 18:28:31 +01:00
Benoit Marty
29f152f349 Fix CI 2020-01-08 18:21:01 +01:00
Benoit Marty
6ca3ba6c9b Merge pull request #806 from Bubu/fix_697
call /join/{roomIdOrAlias} instead of /rooms/{roomId}/join
2020-01-08 18:17:34 +01:00
Benoit Marty
f4492e570d Merge branch 'develop' into fix_697 2020-01-08 18:17:23 +01:00
Benoit Marty
587fefedb5 Merge pull request #809 from Bubu/fix_807
set homeserver field when populating room directory list
2020-01-08 18:12:42 +01:00
Benoit Marty
943be39e1a Merge branch 'develop' into fix_807 2020-01-08 18:12:22 +01:00
Benoit Marty
616f3d3345 Merge pull request #817 from vector-im/feature/fab_scroll
Show skip to bottom FAB while scrolling down (#752)
2020-01-08 18:07:10 +01:00
Benoit Marty
2b8ecae8e3 Fix CI 2020-01-08 18:05:26 +01:00
Benoit Marty
17c4013383 Developer mode: Fail-fast (#745) 2020-01-08 17:58:26 +01:00
Benoit Marty
d662b4a9b4 Exclude play-services-oss-licenses library from F-Droid build (#814) 2020-01-08 15:57:35 +01:00
Benoit Marty
501ac36040 Reduce size of RoomDetailFragment 2020-01-08 15:05:20 +01:00
Benoit Marty
7575cb286e Show skip to bottom FAB while scrolling down (#752) 2020-01-08 15:05:20 +01:00
ganfra
c60b4ddb5a Timeline: don't wait for realm notification to come back, use it right away to init 2020-01-08 13:59:43 +01:00
ganfra
9970d7ffa0 SDK: get some better queries 2020-01-08 11:55:22 +01:00
ganfra
2dd2a8db6c Emoji data source as singleton 2020-01-08 11:54:42 +01:00
Benoit Marty
8ef5c60e2e RageShake is enabled by default 2020-01-08 11:43:21 +01:00
ganfra
03c3c9ae57 Timeline: clear unlinked should use new parameters 2020-01-07 18:15:48 +01:00
ganfra
38c198fe02 Rx: fetch first before returning live data results 2020-01-07 18:15:07 +01:00
ganfra
42c7421b05 Merge branch 'develop' into feature/stabilization 2020-01-07 14:42:38 +01:00
ganfra
19fb3ce032 Merge branch 'develop' into feature/stabilization 2020-01-07 14:28:23 +01:00
Benoit Marty
5a7f4bed43 ktlint 2020-01-07 14:24:26 +01:00
Benoit Marty
03734a7ad5 Merge pull request #802 from vector-im/feature/sessionId
Identify a session with the userId and the deviceId
2020-01-07 14:23:09 +01:00
ganfra
d710106bbb Clean code 2020-01-07 14:09:04 +01:00
ganfra
f09bf61750 Room detail: try to get some better perfs with fetching data. LiveData is slow as we only use one HandlerThread at the time. Might want Realm 7.0 and frozen objects to rework that 2020-01-07 13:31:34 +01:00
ganfra
f9487f8995 Work on timeline 2020-01-06 18:44:04 +01:00
ganfra
99c523b710 Update libs 2020-01-06 18:43:34 +01:00
ganfra
3cc15387ae Realm: compatch on launch 2020-01-06 18:41:09 +01:00
Benoit Marty
6245763577 Merge pull request #789 from vector-im/feature/christmas_fix
Auto completion for emojis
2020-01-06 14:20:44 +01:00
Benoit Marty
448552d287 Move list of Quick Emoji to Emoji Data Source 2020-01-06 13:48:34 +01:00
Benoit Marty
9ecceafb96 Move comment 2020-01-06 13:47:06 +01:00
Benoit Marty
92d7ebe94f Update changes 2020-01-06 13:47:06 +01:00
Benoit Marty
0e5fcd071c Completion on emoji: display the first 50 results 2020-01-06 13:46:37 +01:00
Benoit Marty
c8e67f8ab4 Completion on emoji WIP 2020-01-06 13:46:10 +01:00
Benoit Marty
5fa2acf60b Completion on emoji 2020-01-06 13:46:10 +01:00
Benoit Marty
9e73e95f55 Ensure there is never twice the same emoji 2020-01-06 13:46:10 +01:00
Benoit Marty
8b4c51139d Completion on emoji WIP 2020-01-06 13:46:10 +01:00
Benoit Marty
8597c2b9a2 Improve API 2020-01-06 13:46:10 +01:00
Benoit Marty
d88e5d8af8 DRY 2020-01-06 13:46:10 +01:00
Benoit Marty
c4fe0bdb7f Split into small methods 2020-01-06 13:46:10 +01:00
Benoit Marty
d73a1135ae Extract AutoComplete feature from RoomDetailFragment 2020-01-06 13:46:10 +01:00
Benoit Marty
ed097bcf37 Merge pull request #798 from vector-im/feature/settings_cleanup
Feature/settings cleanup
2020-01-06 13:41:50 +01:00
Benoit Marty
01db856a5d Improve (a bit) the devices list UX/UI 2020-01-06 10:51:30 +01:00
Benoit Marty
a00f51a264 Settings: rename "developer mode" to "advanced settings" 2020-01-06 10:32:36 +01:00
Valere
06b41af467 Merge pull request #793 from vector-im/outgoing_dm_verif
BottomSheet UX for verification
2020-01-06 10:22:27 +01:00
Marcus Hoffmann
9e8217082c set homeserver field when populating room directory list
fixes #807

Signed-off-by: Marcus Hoffmann <bubu@bubu1.eu>
2020-01-06 03:48:53 +01:00
Marcus Hoffmann
ce73007157 call /join/{roomIdOrAlias} instead of /rooms/{roomId}/join
The former endpoint doesn't work for joining over federation, the
server_name parameter is ignored.

Fixes #697

Signed-off-by: Marcus Hoffmann <bubu@bubu1.eu>
2020-01-06 01:01:59 +01:00
Valere
c2cd149299 Fix / accept button was not starting the verify sheet
Was launching start sheet, because request was not known by VerificationService. Due to message observer blocked trying to download keys..
2020-01-03 19:06:23 +01:00
Valere
08ed8d4fa7 Code review 2020-01-03 17:38:33 +01:00
Benoit Marty
f432d15757 Ensure key aliases are always computed the same way 2020-01-03 16:20:43 +01:00
Benoit Marty
215abea10a Introduce @SessionId 2020-01-03 16:20:43 +01:00
Benoit Marty
160927e7b5 Split code into several methods 2020-01-03 16:20:43 +01:00
Benoit Marty
c2e7e33050 Update SessionParamsEntity primaryKey to include deviceId 2020-01-03 16:20:43 +01:00
Benoit Marty
455448806d Merge pull request #799 from vector-im/feature/fix_crash
Fix crash when opening room creation screen from the room filtering screen
2020-01-02 18:57:17 +01:00
Benoit Marty
a969443517 Fix crash when opening room creation screen from the room filtering screen 2020-01-02 18:53:35 +01:00
Benoit Marty
1bd85082c3 Auto-review 2020-01-02 18:45:44 +01:00
Benoit Marty
de1d79b637 Remove Preference divider and cleanup prefs 2020-01-02 18:27:46 +01:00
Benoit Marty
8e478e78e1 Disable pref unused 2020-01-02 18:17:54 +01:00
Benoit Marty
96c9293edc Rageshake: vibrate 2020-01-02 18:15:23 +01:00
Benoit Marty
5c26f66523 Rageshake: settings for sensitivity 2020-01-02 17:42:44 +01:00
Valere
d1233e8470 Fix / tap on accept shows request button instead of start 2020-01-02 17:04:41 +01:00
Benoit Marty
5a24f78c05 Hide non working settings (#751) 2020-01-02 16:24:31 +01:00
Valere
bf28f14b8b Fix / Decline request was not implemented 2020-01-02 16:13:13 +01:00
Benoit Marty
703a1a034d Developer mode: hide show (decrypted) source actions 2020-01-02 16:11:44 +01:00
Benoit Marty
7d744f7d7f Developer mode: UI
And some cleanup
2020-01-02 16:01:47 +01:00
Benoit Marty
8dff196716 Device list: remove the detail dialog: handle the actions directly in the list 2020-01-02 15:44:47 +01:00
Valere
52c25b803f cleaning 2020-01-02 15:16:45 +01:00
Benoit Marty
6b2703f6ce Device list is now on a dedicated Fragment
New request to get info on the current device for VectorSettingsSecurityPrivacyFragment. The whole device list is only retrieved in the new Fragment
2020-01-02 15:05:17 +01:00
Valere
b26318f15c Fix / Cancel messages was not sent 2020-01-02 12:51:12 +01:00
Valere
f541661059 Use workers to send verification messages 2020-01-02 11:52:27 +01:00
ganfra
e32d242e38 Timeline: remove use of isUnlinked method as it slows down the insertion a lot 2019-12-31 12:58:43 +01:00
Valere
5b210df7c5 Manage done states + cleaning 2019-12-31 10:36:10 +01:00
ganfra
787908287c Member events: cache all over the session 2019-12-31 08:07:32 +01:00
Valere
935b3d7f3f cleaning 2019-12-30 20:18:08 +01:00
ganfra
8156b754c1 RecyclerView: introduce view pool 2019-12-30 19:54:39 +01:00
ganfra
03fd474aa8 Member events: try to cache (WIP) 2019-12-30 19:53:36 +01:00
Valere
3c4506cb58 merge madness ?? 2019-12-30 19:52:48 +01:00
Valere
3eed9b5083 cleaning 2019-12-30 18:42:32 +01:00
Valere
6bf3a703df BottomSheet UX 2019-12-30 18:01:06 +01:00
Valere
2152af8851 klint 2019-12-30 17:54:44 +01:00
Valere
5b33a42f8a FIx / missing strings after merge 2019-12-30 17:36:33 +01:00
Valere
a73cd61b96 WIP 2019-12-30 15:16:11 +01:00
Valere
4edd5e3530 Added SAS do not match api 2019-12-30 14:32:58 +01:00
Valere
4c0cbca4cb Support .verification.ready event 2019-12-30 14:32:58 +01:00
Valere
308b15b908 Fix / m.key.verification.key was not sent in toDevice mode 2019-12-30 14:32:04 +01:00
Valere
38906084d1 WIP 2019-12-30 14:32:04 +01:00
Valere
0997d9abf4 Merge branch 'develop' into cross_signing 2019-12-30 14:27:47 +01:00
ganfra
6ad914154a Update some libs 2019-12-30 10:46:25 +01:00
ganfra
cba7e460eb Action bottom sheet: fix deprecated constraints 2019-12-27 18:54:28 +01:00
ganfra
6794173321 Room detail: fix crash with banner 2019-12-27 18:54:07 +01:00
ganfra
92f4288d3e Realm: update realm lib version 2019-12-27 17:16:44 +01:00
ganfra
8109262cbb Home: fix double tab selection 2019-12-27 17:16:30 +01:00
ganfra
037bf45884 Sync: use foreground service on every android version 2019-12-27 17:09:57 +01:00
ganfra
833a5a37a2 Pill: fix blink and clean files 2019-12-27 10:24:58 +01:00
ganfra
00f316ba5d Room members: introduce RoomMemberEntity to be able to query. Still work to do. 2019-12-26 19:51:03 +01:00
ganfra
dfd8181754 Room profile: start creating some classes and use shared action. 2019-12-20 20:28:27 +01:00
Benoit Marty
90f2199eb7 Merge pull request #785 from vector-im/feature/initial_sync
Feature/initial sync
2019-12-20 18:18:32 +01:00
Benoit Marty
63828bc159 Merge branch 'develop' into feature/initial_sync 2019-12-20 17:55:04 +01:00
Benoit Marty
35b4d90e0d ktlint 2019-12-20 17:54:35 +01:00
Benoit Marty
4fe9c52737 Move permission to the main AndroidManifest 2019-12-20 17:54:02 +01:00
Benoit Marty
c54358831f Group throwable extension together 2019-12-20 17:45:32 +01:00
ganfra
a9d016ae79 Merge branch 'develop' into feature/room_profile 2019-12-20 16:43:16 +01:00
Benoit Marty
fcdaf14b01 Merge pull request #786 from vector-im/feature/room_alias_rendering
Autocompletion and pills for rooms and groups
2019-12-20 14:58:38 +01:00
ganfra
83126d5f55 Fix android tests not working 2019-12-20 11:54:59 +01:00
ganfra
e13281dc97 Update CHANGES and clean code 2019-12-20 11:27:26 +01:00
Benoit Marty
3cc65b1e71 ktlint 2019-12-20 11:05:54 +01:00
Benoit Marty
0ccb975d43 Disable MatrixLinkify 2019-12-20 11:04:06 +01:00
Benoit Marty
54f2ac0d8c Better comment 2019-12-20 10:59:41 +01:00
Benoit Marty
3ee5a7f54d Better code 2019-12-20 10:55:08 +01:00
Benoit Marty
3b0624ea40 Fix issue with "in reply to" link 2019-12-20 10:54:48 +01:00
Benoit Marty
c992d32afd Improve algo 2019-12-20 10:23:45 +01:00
Benoit Marty
c5739abe32 Update changes.md 2019-12-20 10:16:17 +01:00
Benoit Marty
3ac473d945 Remove extra blank line 2019-12-20 10:15:53 +01:00
Benoit Marty
c79b35b089 Autocomplete item layout 2019-12-20 10:15:11 +01:00
Benoit Marty
8dce98c538 Autocompletion: group (including pills for groups) 2019-12-20 02:54:48 +01:00
Benoit Marty
543c07fd69 Render pills for room links 2019-12-20 01:23:45 +01:00
Benoit Marty
05a788453f More generic name 2019-12-20 00:47:47 +01:00
Benoit Marty
c31b64771b Autocompletion: disable animation on the recycler view items 2019-12-20 00:42:19 +01:00
Benoit Marty
92f43a591a Autocompletion for room canonical alias 2019-12-20 00:38:42 +01:00
Benoit Marty
3a829bdfe8 Fix command truncation 2019-12-20 00:05:04 +01:00
Benoit Marty
237b22df59 Fix lots of trouble with the completion popup (resize, change mode, etc.) - next step 2019-12-19 20:31:36 +01:00
Benoit Marty
c18be94986 Fix lots of trouble with the completion popup (resize, change mode, etc.) 2019-12-19 20:03:10 +01:00
Benoit Marty
d342356f29 Add missing state events to the list (not sure about the side effects) 2019-12-19 19:48:30 +01:00
Benoit Marty
07817b69c2 Rename some event type 2019-12-19 19:46:09 +01:00
Benoit Marty
e73970d61b Render aliases and canonical alias change in the timeline 2019-12-19 19:39:35 +01:00
ganfra
0eb0870d6c AvatarRenderer: allow to pass GlideRequests too (fix home group avatar) 2019-12-19 18:29:46 +01:00
ganfra
55748a4af4 Merge branch 'develop' into feature/initial_sync 2019-12-19 17:49:45 +01:00
Benoit Marty
51d6b8828d Version++ 2019-12-19 16:46:01 +01:00
Benoit Marty
358fcb6b34 Merge branch 'release/0.11.0' 2019-12-19 16:44:27 +01:00
Benoit Marty
9a7cd3f270 Merge branch 'release/0.11.0' into develop 2019-12-19 16:44:27 +01:00
Benoit Marty
92315a4189 Prepare release 0.11.0 2019-12-19 16:44:14 +01:00
ganfra
a6afd2e904 Timeline: handle failure when navigating to an unknown event (+ clean some files) 2019-12-19 16:10:59 +01:00
Benoit Marty
156cc1aa4a Import Strings from Riot 2019-12-19 15:50:18 +01:00
Benoit Marty
0d36e9d8a6 Merge pull request #779 from vector-im/feature/fix_some_crashes
Fix some crashes and issues
2019-12-19 14:02:19 +01:00
Benoit Marty
13439769a1 Update wording 2019-12-19 14:01:58 +01:00
ganfra
7bb8cb0682 Permalink: fix nav to same room 2019-12-19 13:42:15 +01:00
ganfra
a4ea9a09ad Room factory: add scope to avoid recreate all the dependencies 2019-12-19 13:41:57 +01:00
Benoit Marty
bf69810f8f Bottom sheet event preview for Sticker 2019-12-19 12:05:47 +01:00
Benoit Marty
bb9510e59b Create Size data class 2019-12-19 12:05:30 +01:00
Benoit Marty
4b0dfa49f4 Limit sticker size in the timeline 2019-12-19 11:44:07 +01:00
Benoit Marty
6652965e48 Ignore lint issue 2019-12-19 10:46:11 +01:00
Valere
94125a0215 Merge branch 'develop' into cross_signing 2019-12-19 10:15:47 +01:00
Valere
d97402f757 Merge pull request #767 from vector-im/dm_verif_incoming_timeline
Dm verif incoming timeline
2019-12-19 10:12:55 +01:00
ganfra
5bde7b9f17 Read marker: fix banner visibility when following permalink 2019-12-19 09:58:05 +01:00
ganfra
c8f0c83cd3 Timeline: don't retry automatically to avoid totally blocking pagination 2019-12-19 09:57:49 +01:00
Benoit Marty
b0ff2cb4bb cleanup 2019-12-18 19:31:10 +01:00
Benoit Marty
648691656a Disable click on Stickers (#703) 2019-12-18 19:20:44 +01:00
Benoit Marty
7eae85a394 Add a ZeroItem to avoid automatic scroll when the breadcrumbs are updated from another client 2019-12-18 18:41:46 +01:00
Benoit Marty
123ffe9f9c Cleanup 2019-12-18 17:00:18 +01:00
ganfra
7697278bb2 LiveObservers: launch directly coroutines 2019-12-18 16:59:45 +01:00
Benoit Marty
c48a439eea Add @JvmStatic for performance reasons.
See https://github.com/airbnb/MvRx/wiki/Advanced-Concepts#mvrxviewmodel
2019-12-18 16:03:10 +01:00
Benoit Marty
9d26ba3186 Fix rendering issue with HTML formatted body 2019-12-18 12:33:51 +01:00
Benoit Marty
08970ad8c1 Fix a crash on public room list
It's maybe a workaround, as it should not happen, but at least it will not crash anymore
2019-12-18 09:56:58 +01:00
ganfra
4c88c12cfe Initial sync, start the sync thread or the sync service 2019-12-17 18:46:19 +01:00
Benoit Marty
79f11ad686 Prevent crash when mimetype is null 2019-12-17 17:49:28 +01:00
Benoit Marty
7fa76b9d35 Prevent crash when opening unknown room, which should not happen... 2019-12-17 16:35:04 +01:00
Benoit Marty
65faedb06b BugReport screen: improve UX when description is too short (reported by Matthew) 2019-12-17 14:26:49 +01:00
Benoit Marty
1ceddd9607 Rageshake: log resumed screens and add the log verbosity ON/OFF to the rageshakes data 2019-12-17 14:05:58 +01:00
Benoit Marty
42cdb1db11 Fix crash reported by rageshake: writeToFile may throw exceptions 2019-12-17 12:26:45 +01:00
Benoit Marty
1c727c1ee4 Fix crash reported by rageshake 2019-12-17 10:42:58 +01:00
ganfra
2316c98a65 Merge branch 'develop' into feature/initial_sync 2019-12-16 19:11:04 +01:00
Benoit Marty
a4aa38ee43 Fix new issue on permalink click 2019-12-16 17:14:26 +01:00
Benoit Marty
4a11d028c0 Merge pull request #706 from vector-im/feature/handle_matrix_to
Feature/handle matrix to
2019-12-16 15:50:21 +01:00
Valere
08d005a611 fix merge 2019-12-16 15:44:32 +01:00
Benoit Marty
c286f2a744 ktlint 2019-12-16 15:43:58 +01:00
Valere
89b414e8fe Merge branch 'develop' into dm_verif_incoming_timeline 2019-12-16 15:30:39 +01:00
Benoit Marty
e2b4899b36 Internal review 2019-12-16 15:21:24 +01:00
ganfra
aa82cd2064 Update CHANGES 2019-12-16 15:16:46 +01:00
ganfra
bc568343a2 Open matrix.to with a loader 2019-12-16 15:16:46 +01:00
ganfra
abf0796794 Room alias and matrix.to link: we can now open a room though roomAlias as long as it's a joined one 2019-12-16 15:16:46 +01:00
ganfra
02febfb01b Start handling room alias 2019-12-16 15:09:41 +01:00
ganfra
91c98d4bfb Permalink: start handling permalink from outside the app 2019-12-16 15:05:55 +01:00
Benoit Marty
cfee6a43fd Merge pull request #760 from vector-im/feature/diff_match_patch_submodule
Use diff_match_patch sources as dependency
2019-12-16 15:02:29 +01:00
Benoit Marty
f14f1db0e0 Merge pull request #774 from vector-im/feature/breadcrumbs_fixes
Fix various UI issues
2019-12-16 15:00:21 +01:00
Benoit Marty
3feb2d8980 Merge pull request #768 from vector-im/feature/soft_logout
Handle invalid tokens gracefully
2019-12-16 14:57:11 +01:00
Benoit Marty
9fc3093c2c Fix issues... 2019-12-16 12:39:51 +01:00
Benoit Marty
7d910f2566 Auto review 2019-12-16 11:30:53 +01:00
Benoit Marty
0a0eda3e34 Display first letter of id if display name is empty 2019-12-16 11:08:48 +01:00
Benoit Marty
cecef5b8da Use id to get first letter, if display name is empty 2019-12-16 10:56:25 +01:00
Benoit Marty
c9ed95ed21 MatrixItem: create extension and check ids 2019-12-16 10:50:48 +01:00
Benoit Marty
3dfd6f5a69 Breadcrumbs: increase font size 2019-12-16 10:20:38 +01:00
Benoit Marty
8fc1400bab Improve user color computation and add unit tests 2019-12-14 10:38:50 +01:00
Benoit Marty
3e4b07cec3 Do not display " (IRC)") in display names 2019-12-14 10:19:11 +01:00
Benoit Marty
fbb1846694 Render default room name when it starts with an emoji (#477) 2019-12-13 21:23:18 +01:00
Benoit Marty
b435212c87 Use same default room colors than Riot-Web
And create MatrixItem
2019-12-13 20:50:32 +01:00
ganfra
5dd46e82d7 Sync: make only one big transaction to avoid having bad states 2019-12-13 18:21:44 +01:00
Valere
3727affc15 cleaning 2019-12-13 18:00:04 +01:00
Valere
ff5305ee66 Fix / Verification Msg show fallback text in room summary 2019-12-13 17:56:06 +01:00
Valere
3953022258 Merge branch 'cross_signing' into dm_verif_incoming_timeline 2019-12-13 16:51:53 +01:00
Benoit Marty
1108ad5705 Scroll breadcrumbs to top when opened 2019-12-13 16:50:32 +01:00
Valere
b473aeb475 Merge pull request #765 from vector-im/sdk_reference_aggregation
Aggregate Event References for DM verifications
2019-12-13 16:45:27 +01:00
Valere
289c03e724 Code review 2019-12-13 16:41:55 +01:00
ganfra
fe2be90002 Sync: use the CoroutineSequencer but need more tests 2019-12-13 15:37:38 +01:00
Benoit Marty
f073342954 Cleanup 2019-12-13 15:32:57 +01:00
Benoit Marty
38b40efac3 Using default values 2019-12-13 15:24:44 +01:00
Benoit Marty
e60bda7806 Better archi, better code, less bug... 2019-12-13 15:16:26 +01:00
Benoit Marty
92e60c939d ErrorFormatter: create interface 2019-12-13 14:09:27 +01:00
Benoit Marty
6e4830e325 ErrorFormatter: move it's declaration to VectorBaseFragment
and avoid duplicated code to manage default onError() in Login fragment
2019-12-13 13:58:49 +01:00
Benoit Marty
c6b98f3654 Soft Logout - display hard logout screen 2019-12-13 12:40:15 +01:00
Benoit Marty
12d54140e5 SoftLogout: also handle Unsupported mode 2019-12-13 12:08:37 +01:00
Benoit Marty
1de85daad9 SoftLogout: handle the case where user sign in with SSO on another account 2019-12-13 11:58:02 +01:00
Valere
210dcca0ee Fix / Handling multi open sessions 2019-12-13 11:22:39 +01:00
Valere
872baacfe4 Fix / verification conclusion not showing in non hidden mode 2019-12-13 11:20:19 +01:00
Benoit Marty
050519e998 Soft Logout - add a TODO, waiting for Synapse bugfix 2019-12-13 09:22:24 +01:00
Benoit Marty
1af44ce5f7 cleanip 2019-12-13 01:37:29 +01:00
Benoit Marty
8d1a36425d Cleanup 2019-12-13 01:29:49 +01:00
Benoit Marty
4e74b545ad SoftLogout: recovery with SSO 2019-12-13 01:25:58 +01:00
Benoit Marty
183d6b53bd SoftLogout: start handling SSO 2019-12-13 00:20:54 +01:00
Benoit Marty
14562f7285 SoftLogout: Inherit from Login stuff to get free forgot password functionality 2019-12-13 00:08:21 +01:00
Benoit Marty
17bcd680b0 organise packages 2019-12-12 23:28:54 +01:00
Benoit Marty
954019547d Soft Logout - update comment 2019-12-12 23:25:14 +01:00
Benoit Marty
782635ec8e Keep loading after success 2019-12-12 23:20:11 +01:00
Benoit Marty
e609f4a57e SoftLogout: epoxy: missing elements 2019-12-12 23:17:03 +01:00
Benoit Marty
907fa35547 Cleanup listener 2019-12-12 22:58:27 +01:00
Benoit Marty
00d0c34363 SoftLogout: use Epoxy 2019-12-12 22:58:15 +01:00
Benoit Marty
6811d31a6d Soft Logout - request homeserver login flow 2019-12-12 20:24:46 +01:00
Benoit Marty
a464c910f8 Fix crash with Realm 2019-12-12 19:43:16 +01:00
Valere
975de1dbed Cleaning / klint 2019-12-12 18:48:57 +01:00
Benoit Marty
d69881f321 cleanup 2019-12-12 17:41:16 +01:00
Benoit Marty
efc1f38f8c SoftLogout: adapt wording depending if all keys are backed up or not 2019-12-12 17:39:21 +01:00
Valere
dedc622140 Merge branch 'sdk_reference_aggregation' into dm_verif_incoming_timeline 2019-12-12 15:55:01 +01:00
Benoit Marty
b9e8da1fbb SoftLogout: clear notifications 2019-12-12 15:50:05 +01:00
Benoit Marty
d2fea275d8 SoftLogout: Loading UI 2019-12-12 15:33:22 +01:00
Benoit Marty
a5af949c15 SoftLogout: Store the info that the token is not valid anymore for a faster startup 2019-12-12 15:32:52 +01:00
Valere
9842cac504 More explicit val naming 2019-12-12 15:29:56 +01:00
Valere
35404b9a7f Fix merge 2019-12-12 15:05:13 +01:00
Valere
0afcb60e7d fix rebase 2019-12-12 14:31:01 +01:00
ganfra
eab94b4f03 Sequencer: handle cancellation 2019-12-12 14:30:40 +01:00
Valere
cb595177a9 Fix test compilation 2019-12-12 13:37:17 +01:00
Valere
cb4d52c9fb Aggregate Event References for DM verifications 2019-12-12 13:37:17 +01:00
Valere
d0a3b4663e FIx / room transport was not updating state 2019-12-12 13:37:17 +01:00
Valere
f53b99a423 rebase 2019-12-12 13:37:17 +01:00
Valere
6da0693488 Convert KeyVerificationStart to data class 2019-12-12 13:37:17 +01:00
Valere
ea817ff1c5 fix rebase 2019-12-12 12:04:38 +01:00
Valere
62f0c6edc0 Fix / Use transport to start verification 2019-12-12 12:04:38 +01:00
Valere
e71ad0e515 Simple strategy to Ignore old verification messages 2019-12-12 12:03:59 +01:00
Valere
553604423e Support verification using room transport 2019-12-12 12:03:42 +01:00
Valere
be723256d3 FIx / room transport was not updating state 2019-12-12 10:49:42 +01:00
Valere
819d7182bb rebase 2019-12-12 10:49:42 +01:00
Valere
0a2ffdbdf1 Convert KeyVerificationStart to data class 2019-12-12 10:49:42 +01:00
Valere
4ac7331f3d fix rebase 2019-12-12 10:49:42 +01:00
Valere
7fc57bdf9b Fix / Use transport to start verification 2019-12-12 10:49:42 +01:00
Valere
d370f6d7c8 Simple strategy to Ignore old verification messages 2019-12-12 10:49:42 +01:00
Valere
46ef442139 cleaning 2019-12-12 10:49:42 +01:00
Benoit Marty
5f3dc73440 Code review 2019-12-12 10:49:42 +01:00
Valere
6137a88a6f Support verification using room transport 2019-12-12 10:49:42 +01:00
Valere
82af848c33 Fix / Verification Request Local Echo 2019-12-12 10:27:58 +01:00
ganfra
6b61c95843 Coroutines: introduce a sequencer 2019-12-11 20:39:07 +01:00
Benoit Marty
261b4be287 Follow naming convention 2019-12-11 18:51:46 +01:00
Benoit Marty
205fc0d9d6 Soft Logout - issue with device display name 2019-12-11 18:49:44 +01:00
Benoit Marty
7699560458 Soft Logout - WIP 2019-12-11 18:35:30 +01:00
Benoit Marty
a193b2659d Create Uri extension and cleanup login code 2019-12-11 18:34:21 +01:00
Benoit Marty
bb85d41f05 Password could contain only spaces 2019-12-11 18:34:21 +01:00
Benoit Marty
9bfe904745 InvalidToken: Regular Signed out screen - move class 2019-12-11 18:34:21 +01:00
Benoit Marty
284dc8602f InvalidToken: Regular Signed out screen 2019-12-11 18:34:21 +01:00
Benoit Marty
29087d4a87 InvalidToken: Rework MainActivity args 2019-12-11 18:34:21 +01:00
Benoit Marty
18649ebddb InvalidToken: notify the app - WIP 2019-12-11 18:34:21 +01:00
Benoit Marty
d5935a13ac MatrixError: add some MatrixError from the spec and copy paste documentation 2019-12-11 18:34:21 +01:00
Benoit Marty
670d4dc34e MatrixError: rename the constants to follow the spec 2019-12-11 18:34:21 +01:00
Benoit Marty
5435a1739e SoftLogout: parse the parameter from server response 2019-12-11 18:34:21 +01:00
Benoit Marty
853518fbb2 Version++ 2019-12-11 18:34:06 +01:00
Valere
a673bf092d Show untrusted conclusions 2019-12-11 18:19:32 +01:00
Valere
0776a301ea Incoming DM verification handling in timeline 2019-12-11 16:49:34 +01:00
ganfra
3a269be2ef Sync: fix crash on gplay flavor and reschedule when no network instead of showing a potential notification all the time 2019-12-11 16:24:30 +01:00
Valere
02f03e6b23 Fix test compilation 2019-12-11 16:00:53 +01:00
Valere
8305ce67dd Aggregate Event References for DM verifications 2019-12-11 14:44:31 +01:00
Valere
95deeb1be7 Merge pull request #730 from vector-im/feature/sdk_dm_verification
[SDK] MSC2241 / verification in DMs
2019-12-11 11:10:46 +01:00
Valere
0b93f34fa0 Use diff_match_patch sources as dependency 2019-12-11 10:51:09 +01:00
ganfra
5338f93852 Sync: use a foreground service for initialSync. 2019-12-10 19:52:12 +01:00
Valere
73f0132d5d FIx / room transport was not updating state 2019-12-10 16:37:54 +01:00
Benoit Marty
5915cebd6d Merge branch 'release/0.10.0' into develop 2019-12-10 15:47:35 +01:00
Valere
c462d15bcf rebase 2019-12-10 14:23:56 +01:00
Valere
3cdd373368 Convert KeyVerificationStart to data class 2019-12-10 11:14:52 +01:00
Valere
e14602d1dc fix rebase 2019-12-10 11:14:52 +01:00
Valere
2aa9c3ea22 Fix / Use transport to start verification 2019-12-10 11:14:52 +01:00
Valere
bbd9738452 Simple strategy to Ignore old verification messages 2019-12-10 11:14:52 +01:00
Valere
36c5566b07 cleaning 2019-12-10 11:14:52 +01:00
Benoit Marty
ce63332a2f Code review 2019-12-10 11:14:52 +01:00
Valere
26b4b6e194 Support verification using room transport 2019-12-10 11:14:20 +01:00
ganfra
fd18bcb97f Room profile: branch leave and notifications actions 2019-11-26 17:25:26 +01:00
ganfra
ab4cab05cf Room profile: continue working on UI 2019-11-26 13:22:07 +01:00
ganfra
8aab46804b Profile room: continue working on it (try to get a nice animation) [WIP] 2019-11-15 20:37:36 +01:00
ganfra
0edc953a23 Room profile: start creating epoxy items and implementing UI 2019-11-14 20:13:59 +01:00
ganfra
fa67509ac2 Room profile: start initializing all the classes + move some packages 2019-11-14 15:34:50 +01:00
1257 changed files with 56492 additions and 12749 deletions

View File

@@ -86,3 +86,10 @@ steps:
plugins:
- docker#v3.1.0:
image: "openjdk"
# Check that indonesians files are identical.
# Due to Android issue, the resource folder must be values-in/, and Weblate export data into values-id/.
# If this step fails, it means that Weblate has updated the file in value-id/ so to fix it, copy the file to values-in/
- label: "Indonesian"
command:
- "diff ./vector/src/main/res/values-id/strings.xml ./vector/src/main/res/values-in/strings.xml"

View File

@@ -12,7 +12,7 @@ max_line_length=off
# Comma-separated list of rules to disable (Since 0.34.0)
# Note that rules in any ruleset other than the standard ruleset will need to be prefixed
# by the ruleset identifier.
disabled_rules=no-wildcard-imports,no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation
disabled_rules=no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation
# The following (so far identified) rules are kept:
# no-blank-line-before-rbrace
@@ -30,3 +30,4 @@ disabled_rules=no-wildcard-imports,no-multi-spaces,colon-spacing,chain-wrapping,
# no-empty-class-body
# experimental:multiline-if-else
# experimental:no-empty-first-line-in-method-block
# no-wildcard-imports

View File

@@ -7,4 +7,4 @@
- [ ] Pull request is based on the develop branch
- [ ] Pull request updates [CHANGES.md](https://github.com/vector-im/riotX-android/blob/develop/CHANGES.md)
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst#sign-off)
- [ ] Pull request includes a [sign off](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md#sign-off)

4
.gitignore vendored
View File

@@ -3,8 +3,8 @@
/local.properties
# idea files: exclude everything except dictionnaries
.idea/caches
.idea/codeStyles
.idea/libraries
.idea/inspectionProfiles
.idea/*.xml
.DS_Store
/build
@@ -14,5 +14,3 @@
/tmp
ktlint
.idea/copyright/New_vector.xml
.idea/copyright/profiles_settings.xml

159
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,159 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="160" />
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
</value>
</option>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="true" />
<option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="true" />
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" />
<option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="true" />
<option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="true" />
<option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="true" />
<option name="CONTINUATION_INDENT_IN_ELVIS" value="true" />
<option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="0" />
<option name="IF_RPAREN_ON_NEW_LINE" value="false" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="RIGHT_MARGIN" value="160" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="RIGHT_MARGIN" value="160" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
<option name="CALL_PARAMETERS_WRAP" value="0" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
<option name="METHOD_PARAMETERS_WRAP" value="0" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
<option name="EXTENDS_LIST_WRAP" value="0" />
<option name="METHOD_CALL_CHAIN_WRAP" value="0" />
<option name="ASSIGNMENT_WRAP" value="0" />
<option name="CLASS_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="1" />
</codeStyleSettings>
</code_scheme>
</component>

6
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

6
.idea/copyright/NewVector.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright (c) &amp;#36;today.year New Vector Ltd&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10; http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License." />
<option name="myName" value="NewVector" />
</copyright>
</component>

8
.idea/copyright/profiles_settings.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<component name="CopyrightManager">
<settings default="NewVector">
<LanguageOptions name="XML">
<option name="fileTypeOverride" value="1" />
<option name="prefixLines" value="false" />
</LanguageOptions>
</settings>
</component>

View File

@@ -9,6 +9,8 @@
<w>decryptor</w>
<w>emoji</w>
<w>emojis</w>
<w>fdroid</w>
<w>gplay</w>
<w>hmac</w>
<w>ktlint</w>
<w>linkified</w>
@@ -18,7 +20,10 @@
<w>pbkdf</w>
<w>pkcs</w>
<w>signin</w>
<w>signout</w>
<w>signup</w>
<w>ssss</w>
<w>threepid</w>
</words>
</dictionary>
</component>

View File

@@ -0,0 +1,35 @@
A full developer contributors list can be found [here](https://github.com/vector-im/riotX-android/graphs/contributors).
# Core team:
Even if we try to be able to work on all the functionalities, we have more knowledge about what we have developed ourselves.
## Benoit: Android team leader
[@benoit.marty:matrix.org](https://matrix.to/#/@benoit.marty:matrix.org)
- Android team leader and project leader, Android developer, GitHub community manager.
- Specialist of the account creation, and many other fun features.
- Reviewing and polishing developed features, code quality manager, PRs reviewer, GitHub community manager.
- Release manager on the Play Store
## François: Software architect
[@ganfra:matrix.org](https://matrix.to/#/@ganfra:matrix.org)
- Software architect, Android developer
- First developer on the project.
- Work mainly on the global architecture of the project.
- Specialist of the timeline, and lots of other features.
## Valere: Product manager, Android developer
[@valere35:matrix.org](https://matrix.to/#/@valere35:matrix.org)
- Product manager, Android developer
- Specialist on the crypto implementation.
# Other contributors
First of all, we thank all contributors who use RiotX and report problems on this GitHub project or via the integrated rageshake function.
We do not forget all translators, for their work of translating RiotX into many languages. They are also the authors of RiotX.
Feel free to add your name below, when you contribute to the project!

View File

@@ -1,3 +1,176 @@
Changes in RiotX 0.17.0 (2020-02-27)
===================================================
Features ✨:
- Secured Shared Storage Support (#984, #936)
- It's now possible to select several rooms (with a possible mix of clear/encrypted rooms) when sharing elements to RiotX (#1010)
- Media preview: media are previewed before being sent to a room (#1010)
- Image edition: it's now possible to edit image before sending: crop, rotate, and delete actions are supported (#1010)
- Sending image: image are sent to rooms with a reduced size. It's still possible to send original image file (#1010)
Improvements 🙌:
- Migrate to binary QR code verification (#994)
- Share action is added to room profile and room member profile (#858)
- Display avatar in fullscreen (#861)
- Fix some performance issues with crypto
Bugfix 🐛:
- Account creation: wrongly hints that an email can be used to create an account (#941)
- Fix crash in the room directory, when public room has no name (#1023)
- Fix restoring keys backup with passphrase (#526)
- Fix rotation of full-size image (#647)
- Fix joining rooms from directory via federation isn't working. (#808)
- Leaving a room creates a stuck "leaving room" loading screen. (#1041)
- Fix some invitation handling issues (#1013)
- New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022)
- New direct chat: selecting several participants was not adding the room to the direct chats list
- Room overview shows deleted messages as “Encrypted message” (#758)
SDK API changes ⚠️:
- Get crypto methods through Session.cryptoService()
- ProgressListener.onProgress() function will be invoked on the background thread instead of UI thread
- Improve CreateRoomParams API (#1070)
Changes in RiotX 0.16.0 (2020-02-14)
===================================================
Features ✨:
- Polls and Bot Buttons (MSC 2192 matrix-org/matrix-doc#2192)
Improvements 🙌:
- Show confirmation dialog before deleting a message (#967, #1003)
- Open room member profile from reactions list and read receipts list (#875)
Bugfix 🐛:
- Fix crash by removing all notifications after clearing cache (#878)
- Fix issue with verification when other client declares it can only show QR code (#988)
- Fix too errors in the code (1941862499c9ec5268cc80882512ced379cafcfd, a250a895fe0a4acf08c671e03434edcd29ccd84f)
SDK API changes ⚠️:
- Javadoc improved for PushersService
- PushersService.pushers() has been renamed to PushersService.getPushers()
Changes in RiotX 0.15.0 (2020-02-10)
===================================================
Improvements 🙌:
- Improve navigation to the timeline (#789, #862)
- Improve network detection. It is now based on the sync request status (#873, #882)
Other changes:
- Support SSO login with Firefox account (#606)
Bugfix 🐛:
- Ask for permission before opening the camera (#934)
- Encrypt for invited users by default, if the room state allows it (#803)
Changes in RiotX 0.14.3 (2020-02-03)
===================================================
Bugfix 🐛:
- Fix Exception in DeviceListManager
Changes in RiotX 0.14.2 (2020-02-02)
===================================================
Bugfix 🐛:
- Fix RiotX not starting issue
Changes in RiotX 0.14.1 (2020-02-02)
===================================================
Bugfix 🐛:
- Cross-signing: fix UX issue when closing the bottom sheet verification (#813)
- Room and room member profile: fix issues on dark and black themes
Changes in RiotX 0.14.0 (2020-02-01)
===================================================
Features ✨:
- First implementation of Cross-signing
- Enable encryption in unencrypted rooms, from the room settings (#212)
- Negotiate E2E by default for DMs (#907)
Improvements 🙌:
- Sharing things to RiotX: sort list by recent room first (#771)
- Hide the algorithm when turning on e2e (#897)
- Sort room members by display names
Other changes:
- Add support for /rainbow and /rainbowme commands (#879)
Build 🧱:
- Ensure builds are reproducible (#842)
- F-Droid: fix the "-dev" issue in version name (#815)
Changes in RiotX 0.13.0 (2020-01-17)
===================================================
Features ✨:
- Send and render typing events (#564)
- Create Room Profile screen (#54)
- Create Room Member Profile screen (#59)
Improvements 🙌:
- Render events m.room.encryption and m.room.guest_access in the timeline
Bugfix 🐛:
- Fix broken background sync in F-Droid version
- Fix issue with downloaded file on encrypted rooms. The file was not properly decrypted
Build 🧱:
- Change the way versionCode is computed (#827)
Changes in RiotX 0.12.0 (2020-01-09)
===================================================
Improvements 🙌:
- The initial sync is now handled by a foreground service
- Render aliases and canonical alias change in the timeline
- Introduce developer mode in the settings (#745, #796)
- Improve devices list screen
- Add settings for rageshake sensibility
- Fix autocompletion issues and add support for rooms, groups, and emoji (#780)
- Show skip to bottom FAB while scrolling down (#752)
- Enable encryption on a room, SDK part (#212)
Other changes:
- Change the way RiotX identifies a session to allow the SDK to support several sessions with the same user (#800)
- Exclude play-services-oss-licenses library from F-Droid build (#814)
- Email domain can be limited on some homeservers, i18n of the displayed error (#754)
Bugfix 🐛:
- Fix crash when opening room creation screen from the room filtering screen
- Fix avatar image disappearing (#777)
- Fix read marker banner when permalink
- Fix joining upgraded rooms (#697)
- Fix matrix.org room directory not being browsable (#807)
- Hide non working settings (#751)
Changes in RiotX 0.11.0 (2019-12-19)
===================================================
Features ✨:
- Implement soft logout (#281)
Improvements 🙌:
- Handle navigation to room via room alias (#201)
- Open matrix.to link in RiotX (#57)
- Limit sticker size in the timeline
Other changes:
- Use same default room colors than Riot-Web
Bugfix 🐛:
- Scroll breadcrumbs to top when opened
- Render default room name when it starts with an emoji (#477)
- Do not display " (IRC)" in display names https://github.com/vector-im/riot-android/issues/444
- Fix rendering issue with HTML formatted body
- Disable click on Stickers (#703)
Build 🧱:
- Include diff-match-patch sources as dependency
Changes in RiotX 0.10.0 (2019-12-10)
===================================================
@@ -27,6 +200,7 @@ Changes in RiotX 0.9.0 (2019-12-05)
Features ✨:
- Account creation. It's now possible to create account on any homeserver with RiotX (#34)
- Iteration of the login flow (#613)
- [SDK] MSC2241 / verification in DMs (#707)
Improvements 🙌:
- Send mention Pills from composer
@@ -232,7 +406,7 @@ Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-a
=======================================================
Changes in RiotX 0.0.0 (2019-XX-XX)
Changes in RiotX 0.X.0 (2020-XX-XX)
===================================================
Features ✨:
@@ -241,15 +415,17 @@ Features ✨:
Improvements 🙌:
-
Other changes:
-
Bugfix 🐛:
-
Translations 🗣:
-
SDK API changes ⚠️:
-
Build 🧱:
-
Other changes:
-

View File

@@ -1,6 +1,6 @@
# Contributing code to Matrix
Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst
Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md
Android support can be found in this [![Riot Android Matrix room #riot-android:matrix.org](https://img.shields.io/matrix/riot-android:matrix.org.svg?label=%23riot-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#riot-android:matrix.org) room.
@@ -11,6 +11,7 @@ Dedicated room for RiotX: [![RiotX Android Matrix room #riot-android:matrix.org]
## Android Studio settings
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.
## Compilation

View File

@@ -12,12 +12,13 @@ RiotX is an Android Matrix Client currently in beta but in active development.
It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-android) with a new user experience. RiotX will become the official replacement as soon as all features are implemented.
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.riotx)
[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.riotx)
Nightly build: [![Buildkite](https://badge.buildkite.com/657d3db27364448d69d54f66c690f7788bc6aa80a7628e37f3.svg?branch=develop)](https://buildkite.com/matrix-dot-org/riotx-android/builds?branch=develop)
# New Android SDK
RiotX is based on a new Android SDK fully written in Kotlin (like RiotX). In order to make the early development as fast as possible, RiotX and the new SDK currently share the same git repository. We will make separate repos once the API is stable enough.
RiotX is based on a new Android SDK fully written in Kotlin (like RiotX). In order to make the early development as fast as possible, RiotX and the new SDK currently share the same git repository. We will make separate repos once the SDK is stable enough.
# Roadmap

View File

@@ -10,7 +10,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.3.2'
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
@@ -34,6 +34,10 @@ allprojects {
includeGroupByRegex "com\\.github\\.jaiselrahman"
// And monarchy
includeGroupByRegex "com\\.github\\.Zhuinden"
// And ucrop
includeGroupByRegex "com\\.github\\.yalantis"
// JsonViewer
includeGroupByRegex 'com\\.github\\.BillCarsonFr'
}
}
maven {
@@ -45,18 +49,6 @@ allprojects {
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
google()
jcenter()
maven {
url 'https://repo.adobe.com/nexus/content/repositories/public/'
content {
includeGroupByRegex "diff_match_patch"
}
}
}
tasks.withType(JavaCompile).all {
options.compilerArgs += [
'-Adagger.gradle.incremental=enabled'
]
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
@@ -64,12 +56,6 @@ allprojects {
kotlinOptions.allWarningsAsErrors = true
}
afterEvaluate {
extensions.findByName("kapt")?.arguments {
arg("dagger.gradle.incremental", "enabled")
}
}
}
task clean(type: Delete) {

1
diff-match-patch/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,8 @@
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "8"
targetCompatibility = "8"

File diff suppressed because it is too large Load Diff

View File

@@ -41,6 +41,9 @@ dependencies {
// Paging
implementation "androidx.paging:paging-runtime-ktx:2.1.0"
// Logging
implementation 'com.jakewharton.timber:timber:4.7.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

View File

@@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import io.reactivex.Observable
import io.reactivex.android.MainThreadDisposable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
private class LiveDataObservable<T>(
@@ -60,3 +61,11 @@ private class LiveDataObservable<T>(
fun <T> LiveData<T>.asObservable(): Observable<T> {
return LiveDataObservable(this).observeOn(Schedulers.computation())
}
internal fun <T> Observable<T>.startWithCallable(supplier: () -> T): Observable<T> {
val startObservable = Observable
.fromCallable(supplier)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
return startWith(startObservable)
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2019 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.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.CompletableEmitter
internal class MatrixCallbackCompletable<T>(private val completableEmitter: CompletableEmitter) : MatrixCallback<T> {
override fun onSuccess(data: T) {
completableEmitter.onComplete()
}
override fun onFailure(failure: Throwable) {
completableEmitter.tryOnError(failure)
}
}
fun Cancelable.toCompletable(completableEmitter: CompletableEmitter) {
completableEmitter.setCancellable {
this.cancel()
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2019 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.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.SingleEmitter
internal class MatrixCallbackSingle<T>(private val singleEmitter: SingleEmitter<T>) : MatrixCallback<T> {
override fun onSuccess(data: T) {
singleEmitter.onSuccess(data)
}
override fun onFailure(failure: Throwable) {
singleEmitter.tryOnError(failure)
}
}
fun <T> Cancelable.toSingle(singleEmitter: SingleEmitter<T>) {
singleEmitter.setCancellable {
this.cancel()
}
}

View File

@@ -22,3 +22,9 @@ import io.reactivex.Observable
fun <T : Any> Observable<Optional<T>>.unwrap(): Observable<T> {
return filter { it.hasValue() }.map { it.get() }
}
fun <T : Any, U : Any> Observable<Optional<T>>.mapOptional(fn: (T) -> U?): Observable<Optional<U>> {
return map {
it.map(fn)
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2019 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.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.Completable
import io.reactivex.Single
fun <T> singleBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Single<T> = Single.create {
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
override fun onSuccess(data: T) {
it.onSuccess(data)
}
override fun onFailure(failure: Throwable) {
it.tryOnError(failure)
}
}
val cancelable = builder(callback)
it.setCancellable {
cancelable.cancel()
}
}
fun <T> completableBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Completable = Completable.create {
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
override fun onSuccess(data: T) {
it.onComplete()
}
override fun onFailure(failure: Throwable) {
it.tryOnError(failure)
}
}
val cancelable = builder(callback)
it.setCancellable {
cancelable.cancel()
}
}

View File

@@ -16,33 +16,55 @@
package im.vector.matrix.rx
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
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.notification.RoomNotificationState
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.util.Optional
import im.vector.matrix.android.api.util.toOptional
import io.reactivex.Observable
import io.reactivex.Single
class RxRoom(private val room: Room) {
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
return room.getRoomSummaryLive().asObservable()
return room.getRoomSummaryLive()
.asObservable()
.startWithCallable { room.roomSummary().toOptional() }
}
fun liveRoomMemberIds(): Observable<List<String>> {
return room.getRoomMemberIdsLive().asObservable()
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable<List<RoomMemberSummary>> {
return room.getRoomMembersLive(queryParams).asObservable()
.startWithCallable {
room.getRoomMembers(queryParams)
}
}
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
return room.getEventSummaryLive(eventId).asObservable()
return room.getEventAnnotationsSummaryLive(eventId).asObservable()
.startWithCallable {
room.getEventAnnotationsSummary(eventId).toOptional()
}
}
fun liveTimelineEvent(eventId: String): Observable<Optional<TimelineEvent>> {
return room.getTimeLineEventLive(eventId).asObservable()
.startWithCallable {
room.getTimeLineEvent(eventId).toOptional()
}
}
fun liveStateEvent(eventType: String, stateKey: String): Observable<Optional<Event>> {
return room.getStateEventLive(eventType, stateKey).asObservable()
.startWithCallable {
room.getStateEvent(eventType, stateKey).toOptional()
}
}
fun liveReadMarker(): Observable<Optional<String>> {
@@ -53,13 +75,13 @@ class RxRoom(private val room: Room) {
return room.getMyReadReceiptLive().asObservable()
}
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
fun loadRoomMembersIfNeeded(): Single<Unit> = singleBuilder {
room.loadRoomMembersIfNeeded(it)
}
fun joinRoom(reason: String? = null,
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
room.join(reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
room.join(reason, viaServers, it)
}
fun liveEventReadReceipts(eventId: String): Observable<List<ReadReceipt>> {

View File

@@ -18,68 +18,116 @@ package im.vector.matrix.rx
import androidx.paging.PagedList
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.group.GroupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary
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.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
import io.reactivex.Observable
import io.reactivex.Single
class RxSession(private val session: Session) {
fun liveRoomSummaries(): Observable<List<RoomSummary>> {
return session.liveRoomSummaries().asObservable()
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Observable<List<RoomSummary>> {
return session.getRoomSummariesLive(queryParams).asObservable()
.startWithCallable {
session.getRoomSummaries(queryParams)
}
}
fun liveGroupSummaries(): Observable<List<GroupSummary>> {
return session.liveGroupSummaries().asObservable()
fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Observable<List<GroupSummary>> {
return session.getGroupSummariesLive(queryParams).asObservable()
.startWithCallable {
session.getGroupSummaries(queryParams)
}
}
fun liveBreadcrumbs(): Observable<List<RoomSummary>> {
return session.liveBreadcrumbs().asObservable()
return session.getBreadcrumbsLive().asObservable()
.startWithCallable {
session.getBreadcrumbs()
}
}
fun liveSyncState(): Observable<SyncState> {
return session.syncState().asObservable()
return session.getSyncStateLive().asObservable()
}
fun livePushers(): Observable<List<Pusher>> {
return session.livePushers().asObservable()
return session.getPushersLive().asObservable()
}
fun liveUser(userId: String): Observable<Optional<User>> {
return session.liveUser(userId).asObservable().distinctUntilChanged()
return session.getUserLive(userId).asObservable()
.startWithCallable {
session.getUser(userId).toOptional()
}
}
fun liveUsers(): Observable<List<User>> {
return session.liveUsers().asObservable()
return session.getUsersLive().asObservable()
}
fun liveIgnoredUsers(): Observable<List<User>> {
return session.liveIgnoredUsers().asObservable()
return session.getIgnoredUsersLive().asObservable()
}
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
return session.livePagedUsers(filter).asObservable()
return session.getPagedUsersLive(filter).asObservable()
}
fun createRoom(roomParams: CreateRoomParams): Single<String> = Single.create {
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
session.createRoom(roomParams, it)
}
fun searchUsersDirectory(search: String,
limit: Int,
excludedUserIds: Set<String>): Single<List<User>> = Single.create {
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
excludedUserIds: Set<String>): Single<List<User>> = singleBuilder {
session.searchUsersDirectory(search, limit, excludedUserIds, it)
}
fun joinRoom(roomId: String,
fun joinRoom(roomIdOrAlias: String,
reason: String? = null,
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
session.joinRoom(roomId, reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
session.joinRoom(roomIdOrAlias, reason, viaServers, it)
}
fun getRoomIdByAlias(roomAlias: String,
searchOnServer: Boolean): Single<Optional<String>> = singleBuilder {
session.getRoomIdByAlias(roomAlias, searchOnServer, it)
}
fun getProfileInfo(userId: String): Single<JsonDict> = singleBuilder {
session.getProfile(userId, it)
}
fun liveUserCryptoDevices(userId: String): Observable<List<CryptoDeviceInfo>> {
return session.cryptoService().getLiveCryptoDeviceInfo(userId).asObservable().startWithCallable {
session.cryptoService().getCryptoDeviceInfo(userId)
}
}
fun liveCrossSigningInfo(userId: String): Observable<Optional<MXCrossSigningInfo>> {
return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asObservable()
.startWithCallable {
session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId).toOptional()
}
}
fun liveAccountData(types: Set<String>): Observable<List<UserAccountDataEvent>> {
return session.getLiveAccountDataEvents(types).asObservable()
.startWithCallable {
session.getAccountDataEvents(types)
}
}
}

View File

@@ -10,7 +10,7 @@ buildscript {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:5.12.0"
classpath "io.realm:realm-gradle-plugin:6.1.0"
}
}
@@ -74,7 +74,7 @@ android {
}
static def gitRevision() {
def cmd = "git rev-parse --short HEAD"
def cmd = "git rev-parse --short=8 HEAD"
return cmd.execute().text.trim()
}
@@ -92,41 +92,41 @@ dependencies {
def arrow_version = "0.8.2"
def moshi_version = '1.8.0'
def lifecycle_version = '2.1.0'
def lifecycle_version = '2.2.0'
def arch_version = '2.1.0'
def coroutines_version = "1.3.2"
def markwon_version = '3.1.0'
def daggerVersion = '2.24'
def daggerVersion = '2.25.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.1.0-beta05"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// Network
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-moshi:2.6.2'
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
implementation 'com.novoda:merlin:1.2.0'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
implementation "ru.noties.markwon:core:$markwon_version"
// Image
implementation 'androidx.exifinterface:exifinterface:1.0.0'
implementation 'androidx.exifinterface:exifinterface:1.1.0'
implementation 'id.zelory:compressor:3.0.0'
// Database
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
// Work
implementation "androidx.work:work-runtime-ktx:2.3.0-alpha01"
implementation "androidx.work:work-runtime-ktx:2.3.0"
// FP
implementation "io.arrow-kt:arrow-core:$arrow_version"
@@ -168,7 +168,7 @@ dependencies {
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12'
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
}

View File

@@ -21,6 +21,7 @@ import androidx.test.core.app.ApplicationProvider
import java.io.File
interface InstrumentedTest {
fun context(): Context {
return ApplicationProvider.getApplicationContext()
}

View File

@@ -18,5 +18,8 @@ package im.vector.matrix.android
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.Executors
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main,
Executors.newSingleThreadExecutor().asCoroutineDispatcher())

View File

@@ -0,0 +1,61 @@
/*
* Copyright 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.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants
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 AccountCreationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
@Test
fun createAccountTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
commonTestHelper.signOutAndClose(session)
}
@Test
fun createAccountAndLoginAgainTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
// Log again to the same account
val session2 = commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
commonTestHelper.signOutAndClose(session)
commonTestHelper.signOutAndClose(session2)
}
@Test
fun simpleE2eTest() {
val res = cryptoTestHelper.doE2ETestWithAliceInARoom()
res.cleanUp(commonTestHelper)
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2019 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.auth
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.OkReplayRuleChainNoActivity
import im.vector.matrix.android.api.auth.AuthenticationService
import okreplay.*
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class AuthenticationServiceTest : InstrumentedTest {
lateinit var authenticationService: AuthenticationService
lateinit var okReplayInterceptor: OkReplayInterceptor
private val okReplayConfig = OkReplayConfig.Builder()
.tapeRoot(AndroidTapeRoot(
context(), javaClass))
.defaultMode(TapeMode.READ_WRITE) // or TapeMode.READ_ONLY
.sslEnabled(true)
.interceptor(okReplayInterceptor)
.build()
@get:Rule
val testRule = OkReplayRuleChainNoActivity(okReplayConfig).get()
@Test
@UiThreadTest
@OkReplay(tape = "auth", mode = TapeMode.READ_WRITE)
fun auth() {
}
companion object {
@ClassRule
@JvmField
val grantExternalStoragePermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}

View File

@@ -0,0 +1,301 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2018 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.common
import android.content.Context
import android.net.Uri
import androidx.lifecycle.Observer
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.MatrixConfiguration
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.registration.RegistrationResult
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.LocalEcho
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.model.message.MessageContent
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.api.session.sync.SyncState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import java.util.ArrayList
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
/**
* This class exposes methods to be used in common cases
* Registration, login, Sync, Sending messages...
*/
class CommonTestHelper(context: Context) {
val matrix: Matrix
init {
Matrix.initialize(context, MatrixConfiguration("TestFlavor"))
matrix = Matrix.getInstance(context)
}
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {
return createAccount(userNamePrefix, TestConstants.PASSWORD, testParams)
}
fun logIntoAccount(userId: String, testParams: SessionTestParams): Session {
return logIntoAccount(userId, TestConstants.PASSWORD, testParams)
}
/**
* Create a Home server configuration, with Http connection allowed for test
*/
fun createHomeServerConfig(): HomeServerConnectionConfig {
return HomeServerConnectionConfig.Builder()
.withHomeServerUri(Uri.parse(TestConstants.TESTS_HOME_SERVER_URL))
.build()
}
/**
* This methods init the event stream and check for initial sync
*
* @param session the session to sync
*/
fun syncSession(session: Session) {
val lock = CountDownLatch(1)
session.open()
session.startSync(true)
val syncLiveData = runBlocking(Dispatchers.Main) {
session.getSyncStateLive()
}
val syncObserver = object : Observer<SyncState> {
override fun onChanged(t: SyncState?) {
if (session.hasAlreadySynced()) {
lock.countDown()
syncLiveData.removeObserver(this)
}
}
}
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
await(lock)
}
/**
* Sends text messages in a room
*
* @param room the room where to send the messages
* @param message the message to send
* @param nbOfMessages the number of time the message will be sent
*/
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(nbOfMessages)
val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val newMessages = snapshot
.filter { LocalEcho.isLocalEchoId(it.eventId).not() }
.filter { it.root.getClearType() == EventType.MESSAGE }
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
if (newMessages.size == nbOfMessages) {
sentEvents.addAll(newMessages)
latch.countDown()
}
}
}
val timeline = room.createTimeline(null, TimelineSettings(10))
timeline.start()
timeline.addListener(timelineListener)
for (i in 0 until nbOfMessages) {
room.sendTextMessage(message + " #" + (i + 1))
}
await(latch)
timeline.removeListener(timelineListener)
timeline.dispose()
// Check that all events has been created
assertEquals(nbOfMessages.toLong(), sentEvents.size.toLong())
return sentEvents
}
// PRIVATE METHODS *****************************************************************************
/**
* Creates a unique account
*
* @param userNamePrefix the user name prefix
* @param password the password
* @param testParams test params about the session
* @return the session associated with the newly created account
*/
private fun createAccount(userNamePrefix: String,
password: String,
testParams: SessionTestParams): Session {
val session = createAccountAndSync(
userNamePrefix + "_" + System.currentTimeMillis() + UUID.randomUUID(),
password,
testParams
)
assertNotNull(session)
return session
}
/**
* Logs into an existing account
*
* @param userId the userId to log in
* @param password the password to log in
* @param testParams test params about the session
* @return the session associated with the existing account
*/
private fun logIntoAccount(userId: String,
password: String,
testParams: SessionTestParams): Session {
val session = logAccountAndSync(userId, password, testParams)
assertNotNull(session)
return session
}
/**
* Create an account and a dedicated session
*
* @param userName the account username
* @param password the password
* @param sessionTestParams parameters for the test
*/
private fun createAccountAndSync(userName: String,
password: String,
sessionTestParams: SessionTestParams): Session {
val hs = createHomeServerConfig()
doSync<LoginFlowResult> {
matrix.authenticationService
.getLoginFlow(hs, it)
}
doSync<RegistrationResult> {
matrix.authenticationService
.getRegistrationWizard()
.createAccount(userName, password, null, it)
}
// Preform dummy step
val registrationResult = doSync<RegistrationResult> {
matrix.authenticationService
.getRegistrationWizard()
.dummy(it)
}
assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session
if (sessionTestParams.withInitialSync) {
syncSession(session)
}
return session
}
/**
* Start an account login
*
* @param userName the account username
* @param password the password
* @param sessionTestParams session test params
*/
private fun logAccountAndSync(userName: String,
password: String,
sessionTestParams: SessionTestParams): Session {
val hs = createHomeServerConfig()
doSync<LoginFlowResult> {
matrix.authenticationService
.getLoginFlow(hs, it)
}
val session = doSync<Session> {
matrix.authenticationService
.getLoginWizard()
.login(userName, password, "myDevice", it)
}
if (sessionTestParams.withInitialSync) {
syncSession(session)
}
return session
}
/**
* Await for a latch and ensure the result is true
*
* @param latch
* @throws InterruptedException
*/
fun await(latch: CountDownLatch) {
assertTrue(latch.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
}
// Transform a method with a MatrixCallback to a synchronous method
inline fun <reified T> doSync(block: (MatrixCallback<T>) -> Unit): T {
val lock = CountDownLatch(1)
var result: T? = null
val callback = object : TestMatrixCallback<T>(lock) {
override fun onSuccess(data: T) {
result = data
super.onSuccess(data)
}
}
block.invoke(callback)
await(lock)
assertNotNull(result)
return result!!
}
/**
* Clear all provided sessions
*/
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
fun signOutAndClose(session: Session) {
doSync<Unit> { session.signOut(true, it) }
session.close()
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2018 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.common
import im.vector.matrix.android.api.session.Session
data class CryptoTestData(val firstSession: Session,
val roomId: String,
val secondSession: Session? = null,
val thirdSession: Session? = null) {
fun cleanUp(testHelper: CommonTestHelper) {
testHelper.signOutAndClose(firstSession)
secondSession?.let { testHelper.signOutAndClose(it) }
thirdSession?.let { testHelper.signOutAndClose(it) }
}
}

View File

@@ -0,0 +1,302 @@
/*
* Copyright 2018 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.common
import android.os.SystemClock
import androidx.lifecycle.Observer
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.EventType
import im.vector.matrix.android.api.session.events.model.toContent
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.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
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.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import java.util.HashMap
import java.util.concurrent.CountDownLatch
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
private val defaultSessionParams = SessionTestParams(true)
/**
* @return alice session
*/
fun doE2ETestWithAliceInARoom(): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
val roomId = mTestHelper.doSync<String> {
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it)
}
val room = aliceSession.getRoom(roomId)!!
mTestHelper.doSync<Unit> {
room.enableEncryption(callback = it)
}
return CryptoTestData(aliceSession, roomId)
}
/**
* @return alice and bob sessions
*/
fun doE2ETestWithAliceAndBobInARoom(): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
val lock1 = CountDownLatch(1)
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
}
val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) {
if (t?.isNotEmpty() == true) {
lock1.countDown()
bobRoomSummariesLive.removeObserver(this)
}
}
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver)
}
mTestHelper.doSync<Unit> {
aliceRoom.invite(bobSession.myUserId, callback = it)
}
mTestHelper.await(lock1)
val lock = CountDownLatch(1)
val roomJoinedObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) {
if (bobSession.getRoom(aliceRoomId)
?.getRoomMember(aliceSession.myUserId)
?.membership == Membership.JOIN) {
lock.countDown()
bobRoomSummariesLive.removeObserver(this)
}
}
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(roomJoinedObserver)
}
mTestHelper.doSync<Unit> { bobSession.joinRoom(aliceRoomId, callback = it) }
mTestHelper.await(lock)
// Ensure bob can send messages to the room
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
// assertNotNull(roomFromBobPOV.powerLevels)
// assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
return CryptoTestData(aliceSession, aliceRoomId, bobSession)
}
/**
* @return Alice, Bob and Sam session
*/
fun doE2ETestWithAliceAndBobAndSamInARoom(): CryptoTestData {
val statuses = HashMap<String, String>()
val cryptoTestData = doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val room = aliceSession.getRoom(aliceRoomId)!!
val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
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
SystemClock.sleep(1000)
// samSession.dataHandler.removeListener(samEventListener)
return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
}
/**
* @return Alice and Bob sessions
*/
fun doE2ETestWithAliceAndBobInARoomWithEncryptedMessages(): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val bobSession = cryptoTestData.secondSession!!
bobSession.cryptoService().setWarnOnUnknownDevices(false)
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
val lock = CountDownLatch(1)
val bobEventsListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
// noop
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val messages = snapshot.filter { it.root.getClearType() == EventType.MESSAGE }
.groupBy { it.root.senderId!! }
// Alice has sent 2 messages and Bob has sent 3 messages
if (messages[aliceSession.myUserId]?.size == 2 && messages[bobSession.myUserId]?.size == 3) {
lock.countDown()
}
}
}
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(20))
bobTimeline.start()
bobTimeline.addListener(bobEventsListener)
// Alice sends a message
roomFromAlicePOV.sendTextMessage(messagesFromAlice[0])
// Bob send 3 messages
roomFromBobPOV.sendTextMessage(messagesFromBob[0])
roomFromBobPOV.sendTextMessage(messagesFromBob[1])
roomFromBobPOV.sendTextMessage(messagesFromBob[2])
// Alice sends a message
roomFromAlicePOV.sendTextMessage(messagesFromAlice[1])
mTestHelper.await(lock)
bobTimeline.removeListener(bobEventsListener)
bobTimeline.dispose()
return cryptoTestData
}
fun checkEncryptedEvent(event: Event, roomId: String, clearMessage: String, senderSession: Session) {
assertEquals(EventType.ENCRYPTED, event.type)
assertNotNull(event.content)
val eventWireContent = event.content.toContent()
assertNotNull(eventWireContent)
assertNull(eventWireContent.get("body"))
assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent.get("algorithm"))
assertNotNull(eventWireContent.get("ciphertext"))
assertNotNull(eventWireContent.get("session_id"))
assertNotNull(eventWireContent.get("sender_key"))
assertEquals(senderSession.sessionParams.credentials.deviceId, eventWireContent.get("device_id"))
assertNotNull(event.eventId)
assertEquals(roomId, event.roomId)
assertEquals(EventType.MESSAGE, event.getClearType())
// TODO assertTrue(event.getAge() < 10000)
val eventContent = event.toContent()
assertNotNull(eventContent)
assertEquals(clearMessage, eventContent.get("body"))
assertEquals(senderSession.myUserId, event.senderId)
}
fun createFakeMegolmBackupAuthData(): MegolmBackupAuthData {
return MegolmBackupAuthData(
publicKey = "abcdefg",
signatures = mapOf("something" to mapOf("ed25519:something" to "hijklmnop"))
)
}
fun createFakeMegolmBackupCreationInfo(): MegolmBackupCreationInfo {
return MegolmBackupCreationInfo(
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP,
authData = createFakeMegolmBackupAuthData()
)
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2019 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.common
import okhttp3.Interceptor
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import javax.net.ssl.HttpsURLConnection
/**
* Allows to intercept network requests for test purpose by
* - re-writing the response
* - changing the response code (200/404/etc..).
* - Test delays..
*
* Basic usage:
* <code>
* val mockInterceptor = MockOkHttpInterceptor()
* mockInterceptor.addRule(MockOkHttpInterceptor.SimpleRule(".well-known/matrix/client", 200, "{}"))
*
* RestHttpClientFactoryProvider.defaultProvider = RestClientHttpClientFactory(mockInterceptor)
* AutoDiscovery().findClientConfig("matrix.org", <callback>)
* </code>
*/
class MockOkHttpInterceptor : Interceptor {
private var rules: ArrayList<Rule> = ArrayList()
fun addRule(rule: Rule) {
rules.add(rule)
}
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
rules.forEach { rule ->
if (originalRequest.url.toString().contains(rule.match)) {
rule.process(originalRequest)?.let {
return it
}
}
}
return chain.proceed(originalRequest)
}
abstract class Rule(val match: String) {
abstract fun process(originalRequest: Request): Response?
}
/**
* Simple rule that reply with the given body for any request that matches the match param
*/
class SimpleRule(match: String,
private val code: Int = HttpsURLConnection.HTTP_OK,
private val body: String = "{}") : Rule(match) {
override fun process(originalRequest: Request): Response? {
return Response.Builder()
.protocol(Protocol.HTTP_1_1)
.request(originalRequest)
.message("mocked answer")
.body(body.toResponseBody(null))
.code(code)
.build()
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2018 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.common
data class SessionTestParams @JvmOverloads constructor(val withInitialSync: Boolean = false)

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2018 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.common
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.fail
/**
* Compare two lists and their content
*/
fun assertListEquals(list1: List<Any>?, list2: List<Any>?) {
if (list1 == null) {
assertNull(list2)
} else {
assertNotNull(list2)
assertEquals("List sizes must match", list1.size, list2!!.size)
for (i in list1.indices) {
assertEquals("Elements at index $i are not equal", list1[i], list2[i])
}
}
}
/**
* Compare two maps and their content
*/
fun assertDictEquals(dict1: Map<String, Any>?, dict2: Map<String, Any>?) {
if (dict1 == null) {
assertNull(dict2)
} else {
assertNotNull(dict2)
assertEquals("Map sizes must match", dict1.size, dict2!!.size)
for (i in dict1.keys) {
assertEquals("Values for key $i are not equal", dict1[i], dict2[i])
}
}
}
/**
* Compare two byte arrays content.
* Note that if the arrays have not the same size, it also fails.
*/
fun assertByteArrayNotEqual(a1: ByteArray, a2: ByteArray) {
if (a1.size != a2.size) {
fail("Arrays have not the same size.")
}
for (index in a1.indices) {
if (a1[index] != a2[index]) {
// Difference found!
return
}
}
fail("Arrays are equals.")
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2018 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.common
import android.os.Debug
object TestConstants {
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
// Time out to use when waiting for server response. 10s
private const val AWAIT_TIME_OUT_MILLIS = 10_000
// 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
const val USER_ALICE = "Alice"
const val USER_BOB = "Bob"
const val USER_SAM = "Sam"
const val PASSWORD = "password"
val timeOutMillis: Long
get() = if (Debug.isDebuggerConnected()) {
// Wait more
AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS.toLong()
} else {
AWAIT_TIME_OUT_MILLIS.toLong()
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2018 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.common
import androidx.annotation.CallSuper
import im.vector.matrix.android.api.MatrixCallback
import org.junit.Assert.fail
import timber.log.Timber
import java.util.concurrent.CountDownLatch
/**
* Simple implementation of MatrixCallback, which count down the CountDownLatch on each API callback
* @param onlySuccessful true to fail if an error occurs. This is the default behavior
* @param <T>
*/
open class TestMatrixCallback<T>(private val countDownLatch: CountDownLatch,
private val onlySuccessful: Boolean = true) : MatrixCallback<T> {
@CallSuper
override fun onSuccess(data: T) {
countDownLatch.countDown()
}
@CallSuper
override fun onFailure(failure: Throwable) {
Timber.e(failure, "TestApiCallback")
if (onlySuccessful) {
fail("onFailure " + failure.localizedMessage)
}
countDownLatch.countDown()
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2019 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 android.os.MemoryFile
import android.util.Base64
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import java.io.ByteArrayInputStream
import java.io.InputStream
/**
* Unit tests AttachmentEncryptionTest.
*/
@Suppress("SpellCheckingInspection")
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AttachmentEncryptionTest {
private fun checkDecryption(input: String, encryptedFileInfo: EncryptedFileInfo): String {
val `in` = Base64.decode(input, Base64.DEFAULT)
val inputStream: InputStream
inputStream = if (`in`.isEmpty()) {
ByteArrayInputStream(`in`)
} else {
val memoryFile = MemoryFile("file" + System.currentTimeMillis(), `in`.size)
memoryFile.outputStream.write(`in`)
memoryFile.inputStream
}
val decryptedStream = MXEncryptedAttachments.decryptAttachment(inputStream, encryptedFileInfo)
assertNotNull(decryptedStream)
val buffer = ByteArray(100)
val len = decryptedStream!!.read(buffer)
decryptedStream.close()
return Base64.encodeToString(buffer, 0, len, Base64.DEFAULT).replace("\n".toRegex(), "").replace("=".toRegex(), "")
}
@Test
fun checkDecrypt1() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "AAAAAAAAAAAAAAAAAAAAAA",
url = "dummyUrl"
)
assertEquals("", checkDecryption("", encryptedFileInfo))
}
@Test
fun checkDecrypt2() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "YzF08lARDdOCzJpzuSwsjTNlQc4pHxpdHcXiD/wpK6k"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "__________________________________________8",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "//////////8AAAAAAAAAAA",
url = "dummyUrl"
)
assertEquals("SGVsbG8sIFdvcmxk", checkDecryption("5xJZTt5cQicm+9f4", encryptedFileInfo))
}
@Test
fun checkDecrypt3() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "IOq7/dHHB+mfHfxlRY5XMeCWEwTPmlf4cJcgrkf6fVU"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "__________________________________________8",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "//////////8AAAAAAAAAAA",
url = "dummyUrl"
)
assertEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption("zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q",
encryptedFileInfo))
}
@Test
fun checkDecrypt4() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "LYG/orOViuFwovJpv2YMLSsmVKwLt7pY3f8SYM7KU5E"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "__________________________________________8",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "/////////////////////w",
url = "dummyUrl"
)
assertNotEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption("tJVNBVJ/vl36UQt4Y5e5m84bRUrQHhcdLPvS/7EkDvlkDLZXamBB6k8THbiawiKZ5Mnq9PZMSSbgOCvmnUBOMA",
encryptedFileInfo))
}
}

View File

@@ -16,20 +16,35 @@
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.internal.crypto.model.OlmSessionWrapper
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import org.junit.Assert.*
import io.realm.Realm
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmManager
import org.matrix.olm.OlmSession
private const val DUMMY_DEVICE_KEY = "DeviceKey"
class CryptoStoreTest {
@RunWith(AndroidJUnit4::class)
class CryptoStoreTest : InstrumentedTest {
private val cryptoStoreHelper = CryptoStoreHelper()
@Before
fun setup() {
Realm.init(context())
}
@Test
fun test_metadata_realm_ok() {
val cryptoStore: IMXCryptoStore = cryptoStoreHelper.createStore()

View File

@@ -0,0 +1,208 @@
/*
* Copyright 2018 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 org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
/**
* Unit tests ExportEncryptionTest.
*/
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ExportEncryptionTest {
@Test
fun checkExportError1() {
val password = "password"
val input = "-----"
var failed = false
try {
MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
failed = true
}
assertTrue(failed)
}
@Test
fun checkExportError2() {
val password = "password"
val input = "-----BEGIN MEGOLM SESSION DATA-----\n" + "-----"
var failed = false
try {
MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
failed = true
}
assertTrue(failed)
}
@Test
fun checkExportError3() {
val password = "password"
val input = "-----BEGIN MEGOLM SESSION DATA-----\n" +
" AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" +
" cissyYBxjsfsAn\n" +
" -----END MEGOLM SESSION DATA-----"
var failed = false
try {
MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
failed = true
}
assertTrue(failed)
}
@Test
fun checkExportDecrypt1() {
val password = "password"
val input = "-----BEGIN MEGOLM SESSION DATA-----\nAXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" + "cissyYBxjsfsAndErh065A8=\n-----END MEGOLM SESSION DATA-----"
val expectedString = "plain"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
fail("## checkExportDecrypt1() failed : " + e.message)
}
assertEquals("## checkExportDecrypt1() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportDecrypt2() {
val password = "betterpassword"
val input = "-----BEGIN MEGOLM SESSION DATA-----\nAW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" + "KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n-----END MEGOLM SESSION DATA-----"
val expectedString = "Hello, World"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
fail("## checkExportDecrypt2() failed : " + e.message)
}
assertEquals("## checkExportDecrypt2() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportDecrypt3() {
val password = "SWORDFISH"
val input = "-----BEGIN MEGOLM SESSION DATA-----\nAXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" + "MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\nPgg29363BGR+/Ripq/VCLKGNbw==\n-----END MEGOLM SESSION DATA-----"
val expectedString = "alphanumericallyalphanumericallyalphanumericallyalphanumerically"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
fail("## checkExportDecrypt3() failed : " + e.message)
}
assertEquals("## checkExportDecrypt3() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt1() {
val password = "password"
val expectedString = "plain"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt1() failed : " + e.message)
}
assertEquals("## checkExportEncrypt1() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt2() {
val password = "betterpassword"
val expectedString = "Hello, World"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt2() failed : " + e.message)
}
assertEquals("## checkExportEncrypt2() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt3() {
val password = "SWORDFISH"
val expectedString = "alphanumericallyalphanumericallyalphanumericallyalphanumerically"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt3() failed : " + e.message)
}
assertEquals("## checkExportEncrypt3() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt4() {
val password = "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword"
val expectedString = "alphanumericallyalphanumericallyalphanumericallyalphanumerically"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt4() failed : " + e.message)
}
assertEquals("## checkExportEncrypt4() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
}

View File

@@ -0,0 +1,161 @@
package im.vector.matrix.android.internal.crypto.crosssigning
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.common.CommonTestHelper
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.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class XSigningTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Test
fun test_InitializeAndStoreKeys() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
), it)
}
val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys()
val masterPubKey = myCrossSigningKeys?.masterKey()
assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey)
val selfSigningKey = myCrossSigningKeys?.selfSigningKey()
assertNotNull("SelfSigned key should be stored", selfSigningKey?.unpaddedBase64PublicKey)
val userKey = myCrossSigningKeys?.userKey()
assertNotNull("User key should be stored", userKey?.unpaddedBase64PublicKey)
assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted() == true)
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
mTestHelper.signOutAndClose(aliceSession)
}
@Test
fun test_CrossSigningCheckBobSeesTheKeys() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceAuthParams = UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
)
val bobAuthParams = UserPasswordAuth(
user = bobSession!!.myUserId,
password = TestConstants.PASSWORD
)
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
// Check that alice can see bob keys
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey())
assertNull("Alice should not see bob User key", bobKeysFromAlicePOV.userKey())
assertNotNull("Alice can see bob SelfSigned key", bobKeysFromAlicePOV.selfSigningKey())
assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.masterKey()?.unpaddedBase64PublicKey, bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey)
assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey)
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
mTestHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession)
}
@Test
fun test_CrossSigningTestAliceTrustBobNewDevice() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceAuthParams = UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
)
val bobAuthParams = UserPasswordAuth(
user = bobSession!!.myUserId,
password = TestConstants.PASSWORD
)
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
// Check that alice can see bob keys
val bobUserId = bobSession.myUserId
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) }
// Now bobs logs in on a new device and verifies it
// 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 bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId!!
// Check that bob first session sees the new login
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
}
if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
fail("Bob should see the new device")
}
val bobSecondDevicePOVFirstDevice = bobSession.cryptoService().getDeviceInfo(bobUserId, bobSecondDeviceId)
assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice)
// Manually mark it as trusted from first session
mTestHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
}
// Now alice should cross trust bob's second device
val data2 = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
}
// check that the device is seen
if (data2.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
fail("Alice should see the new device")
}
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
mTestHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession)
mTestHelper.signOutAndClose(bobSession2)
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2019 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 androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.common.assertByteArrayNotEqual
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
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.OlmManager
import org.matrix.olm.OlmPkDecryption
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class KeysBackupPasswordTest : InstrumentedTest {
@Before
fun ensureLibLoaded() {
OlmManager()
}
/**
* Check KeysBackupPassword utilities
*/
@Test
fun passwordConverter_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation
val retrievedPrivateKey = retrievePrivateKeyWithPassword(PASSWORD,
generatePrivateKeyResult.salt,
generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertArrayEquals(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check generatePrivateKeyWithPassword progress listener behavior
*/
@Test
fun passwordConverter_progress_ok() {
val progressValues = ArrayList<Int>(101)
var lastTotal = 0
generatePrivateKeyWithPassword(PASSWORD, object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
if (!progressValues.contains(progress)) {
progressValues.add(progress)
}
lastTotal = total
}
})
assertEquals(100, lastTotal)
// Ensure all values are here
assertEquals(101, progressValues.size)
for (i in 0..100) {
assertTrue(progressValues[i] == i)
}
}
/**
* Check KeysBackupPassword utilities, with bad password
*/
@Test
fun passwordConverter_badPassword_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation, with bad password
val retrievedPrivateKey = retrievePrivateKeyWithPassword(BAD_PASSWORD,
generatePrivateKeyResult.salt,
generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check KeysBackupPassword utilities, with bad password
*/
@Test
fun passwordConverter_badIteration_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation, with bad iteration
val retrievedPrivateKey = retrievePrivateKeyWithPassword(PASSWORD,
generatePrivateKeyResult.salt,
500_001)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check KeysBackupPassword utilities, with bad salt
*/
@Test
fun passwordConverter_badSalt_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation, with bad iteration
val retrievedPrivateKey = retrievePrivateKeyWithPassword(PASSWORD,
BAD_SALT,
generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check [retrievePrivateKeyWithPassword] with data coming from another platform (RiotWeb).
*/
@Test
fun passwordConverter_crossPlatform_ok() {
val password = "This is a passphrase!"
val salt = "TO0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"
val iteration = 500_000
val retrievedPrivateKey = retrievePrivateKeyWithPassword(password, salt, iteration)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
// Data from RiotWeb
val privateKeyBytes = byteArrayOf(
116.toByte(), 224.toByte(), 229.toByte(), 224.toByte(), 9.toByte(), 3.toByte(), 178.toByte(), 162.toByte(),
120.toByte(), 23.toByte(), 108.toByte(), 218.toByte(), 22.toByte(), 61.toByte(), 241.toByte(), 200.toByte(),
235.toByte(), 173.toByte(), 236.toByte(), 100.toByte(), 115.toByte(), 247.toByte(), 33.toByte(), 132.toByte(),
195.toByte(), 154.toByte(), 64.toByte(), 158.toByte(), 184.toByte(), 148.toByte(), 20.toByte(), 85.toByte())
assertArrayEquals(privateKeyBytes, retrievedPrivateKey)
}
companion object {
private const val PASSWORD = "password"
private const val BAD_PASSWORD = "passw0rd"
private const val BAD_SALT = "AA0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2019 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.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import java.util.concurrent.CountDownLatch
/**
* This class observe the state change of a KeysBackup object and provide a method to check the several state change
* It checks all state transitions and detected forbidden transition
*/
internal class StateObserver(private val keysBackup: KeysBackupService,
private val latch: CountDownLatch? = null,
private val expectedStateChange: Int = -1) : KeysBackupStateListener {
private val allowedStateTransitions = listOf(
KeysBackupState.BackingUp to KeysBackupState.ReadyToBackUp,
KeysBackupState.BackingUp to KeysBackupState.WrongBackUpVersion,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.Disabled,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.NotTrusted,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.ReadyToBackUp,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.Unknown,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.WrongBackUpVersion,
KeysBackupState.Disabled to KeysBackupState.Enabling,
KeysBackupState.Enabling to KeysBackupState.Disabled,
KeysBackupState.Enabling to KeysBackupState.ReadyToBackUp,
KeysBackupState.NotTrusted to KeysBackupState.CheckingBackUpOnHomeserver,
// This transition happens when we trust the device
KeysBackupState.NotTrusted to KeysBackupState.ReadyToBackUp,
KeysBackupState.ReadyToBackUp to KeysBackupState.WillBackUp,
KeysBackupState.Unknown to KeysBackupState.CheckingBackUpOnHomeserver,
KeysBackupState.WillBackUp to KeysBackupState.BackingUp,
KeysBackupState.WrongBackUpVersion to KeysBackupState.CheckingBackUpOnHomeserver,
// FIXME These transitions are observed during test, and I'm not sure they should occur. Don't have time to investigate now
KeysBackupState.ReadyToBackUp to KeysBackupState.BackingUp,
KeysBackupState.ReadyToBackUp to KeysBackupState.ReadyToBackUp,
KeysBackupState.WillBackUp to KeysBackupState.ReadyToBackUp,
KeysBackupState.WillBackUp to KeysBackupState.Unknown
)
private val stateList = ArrayList<KeysBackupState>()
private var lastTransitionError: String? = null
init {
keysBackup.addListener(this)
}
// TODO Make expectedStates mandatory to enforce test
fun stopAndCheckStates(expectedStates: List<KeysBackupState>?) {
keysBackup.removeListener(this)
expectedStates?.let {
assertEquals(it.size, stateList.size)
for (i in it.indices) {
assertEquals("The state $i is not correct. states: " + stateList.joinToString(separator = " "), it[i], stateList[i])
}
}
assertNull("states: " + stateList.joinToString(separator = " "), lastTransitionError)
}
override fun onStateChange(newState: KeysBackupState) {
stateList.add(newState)
// Check that state transition is valid
if (stateList.size >= 2
&& !allowedStateTransitions.contains(stateList[stateList.size - 2] to newState)) {
// Forbidden transition detected
lastTransitionError = "Forbidden transition detected from " + stateList[stateList.size - 2] + " to " + newState
}
if (expectedStateChange == stateList.size) {
latch?.countDown()
}
}
}

View File

@@ -0,0 +1,363 @@
/*
* 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.ssss
import androidx.lifecycle.Observer
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
import im.vector.matrix.android.api.session.securestorage.KeySigner
import im.vector.matrix.android.api.session.securestorage.RawBytesKeySpec
import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
import im.vector.matrix.android.api.util.Optional
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 im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.amshove.kluent.shouldBe
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
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 QuadSTests : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val emptyKeySigner = object : KeySigner {
override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
return null
}
}
@Test
fun test_Generate4SKey() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService
val TEST_KEY_ID = "my.test.Key"
mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it)
}
// Assert Account data is updated
val accountDataLock = CountDownLatch(1)
var accountData: UserAccountDataEvent? = null
val liveAccountData = runBlocking(Dispatchers.Main) {
aliceSession.getLiveAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
}
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
accountData = t.getOrNull()
accountDataLock.countDown()
}
}
GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
mTestHelper.await(accountDataLock)
assertNotNull("Key should be stored in account data", accountData)
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
assertNotNull("Key Content cannot be parsed", parsed)
assertEquals("Unexpected Algorithm", SSSS_ALGORITHM_AES_HMAC_SHA2, parsed!!.algorithm)
assertEquals("Unexpected key name", "Test Key", parsed.name)
assertNull("Key was not generated from passphrase", parsed.passphrase)
// Set as default key
quadS.setDefaultKey(TEST_KEY_ID, object : MatrixCallback<Unit> {})
var defaultKeyAccountData: UserAccountDataEvent? = null
val defaultDataLock = CountDownLatch(1)
val liveDefAccountData = runBlocking(Dispatchers.Main) {
aliceSession.getLiveAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
}
val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
defaultKeyAccountData = t.getOrNull()!!
defaultDataLock.countDown()
}
}
GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) }
mTestHelper.await(defaultDataLock)
assertNotNull(defaultKeyAccountData?.content)
assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
mTestHelper.signOutAndClose(aliceSession)
}
@Test
fun test_StoreSecret() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId = "My.Key"
val info = generatedSecret(aliceSession, keyId, true)
val keySpec = RawBytesKeySpec.fromRecoveryKey(info.recoveryKey)
// Store a secret
val clearSecret = "42".toByteArray().toBase64NoPadding()
mTestHelper.doSync<Unit> {
aliceSession.sharedSecretStorageService.storeSecret(
"secret.of.life",
clearSecret,
listOf(SharedSecretStorageService.KeyRef(null, keySpec)), // default key
it
)
}
val secretAccountData = assertAccountData(aliceSession, "secret.of.life")
val encryptedContent = secretAccountData.content.get("encrypted") as? Map<*, *>
assertNotNull("Element should be encrypted", encryptedContent)
assertNotNull("Secret should be encrypted with default key", encryptedContent?.get(keyId))
val secret = EncryptedSecretContent.fromJson(encryptedContent?.get(keyId))
assertNotNull(secret?.ciphertext)
assertNotNull(secret?.mac)
assertNotNull(secret?.initializationVector)
// Try to decrypt??
val decryptedSecret = mTestHelper.doSync<String> {
aliceSession.sharedSecretStorageService.getSecret(
"secret.of.life",
null, // default key
keySpec!!,
it
)
}
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
mTestHelper.signOutAndClose(aliceSession)
}
@Test
fun test_SetDefaultLocalEcho() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService
val TEST_KEY_ID = "my.test.Key"
mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it)
}
// Test that we don't need to wait for an account data sync to access directly the keyid from DB
mTestHelper.doSync<Unit> {
quadS.setDefaultKey(TEST_KEY_ID, it)
}
mTestHelper.signOutAndClose(aliceSession)
}
@Test
fun test_StoreSecretWithMultipleKey() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId1 = "Key.1"
val key1Info = generatedSecret(aliceSession, keyId1, true)
val keyId2 = "Key2"
val key2Info = generatedSecret(aliceSession, keyId2, true)
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
mTestHelper.doSync<Unit> {
aliceSession.sharedSecretStorageService.storeSecret(
"my.secret",
mySecretText.toByteArray().toBase64NoPadding(),
listOf(
SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)),
SharedSecretStorageService.KeyRef(keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey))
),
it
)
}
val accountDataEvent = aliceSession.getAccountDataEvent("my.secret")
val encryptedContent = accountDataEvent?.content?.get("encrypted") as? Map<*, *>
assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
assertNotNull(encryptedContent?.get(keyId1))
assertNotNull(encryptedContent?.get(keyId2))
// Assert that can decrypt with both keys
mTestHelper.doSync<String> {
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1,
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
it
)
}
mTestHelper.doSync<String> {
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId2,
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
it
)
}
mTestHelper.signOutAndClose(aliceSession)
}
@Test
fun test_GetSecretWithBadPassphrase() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId1 = "Key.1"
val passphrase = "The good pass phrase"
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
mTestHelper.doSync<Unit> {
aliceSession.sharedSecretStorageService.storeSecret(
"my.secret",
mySecretText.toByteArray().toBase64NoPadding(),
listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey))),
it
)
}
val decryptCountDownLatch = CountDownLatch(1)
var error = false
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1,
RawBytesKeySpec.fromPassphrase(
"A bad passphrase",
key1Info.content?.passphrase?.salt ?: "",
key1Info.content?.passphrase?.iterations ?: 0,
null),
object : MatrixCallback<String> {
override fun onSuccess(data: String) {
decryptCountDownLatch.countDown()
}
override fun onFailure(failure: Throwable) {
error = true
decryptCountDownLatch.countDown()
}
}
)
mTestHelper.await(decryptCountDownLatch)
error shouldBe true
// Now try with correct key
mTestHelper.doSync<String> {
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1,
RawBytesKeySpec.fromPassphrase(
passphrase,
key1Info.content?.passphrase?.salt ?: "",
key1Info.content?.passphrase?.iterations ?: 0,
null),
it
)
}
mTestHelper.signOutAndClose(aliceSession)
}
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
val accountDataLock = CountDownLatch(1)
var accountData: UserAccountDataEvent? = null
val liveAccountData = runBlocking(Dispatchers.Main) {
session.getLiveAccountDataEvent(type)
}
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
if (t?.getOrNull()?.type == type) {
accountData = t.getOrNull()
accountDataLock.countDown()
}
}
GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
mTestHelper.await(accountDataLock)
assertNotNull("Account Data type:$type should be found", accountData)
return accountData!!
}
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService
val creationInfo = mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKey(keyId, keyId, emptyKeySigner, it)
}
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
if (asDefault) {
mTestHelper.doSync<Unit> {
quadS.setDefaultKey(keyId, it)
}
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
}
return creationInfo
}
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService
val creationInfo = mTestHelper.doSync<SsssKeyCreationInfo> {
quadS.generateKeyWithPassphrase(
keyId,
keyId,
passphrase,
emptyKeySigner,
null,
it)
}
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
if (asDefault) {
val setDefaultLatch = CountDownLatch(1)
quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
mTestHelper.await(setDefaultLatch)
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
}
return creationInfo
}
}

View File

@@ -0,0 +1,519 @@
/*
* Copyright 2019 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.verification
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.session.Session
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.OutgoingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.verification.SasMode
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.Event
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
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.rest.KeyVerificationAccept
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
import im.vector.matrix.android.internal.crypto.model.rest.toValue
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
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.NAME_ASCENDING)
class SASTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Test
fun test_aliceStartThenAliceCancel() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val bobTxCreatedLatch = CountDownLatch(1)
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
bobTxCreatedLatch.countDown()
}
}
bobVerificationService.addListener(bobListener)
val txID = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS,
bobSession.myUserId,
bobSession.cryptoService().getMyDevice().deviceId,
null)
assertNotNull("Alice should have a started transaction", txID)
val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!)
assertNotNull("Alice should have a started transaction", aliceKeyTx)
mTestHelper.await(bobTxCreatedLatch)
bobVerificationService.removeListener(bobListener)
val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)
assertNotNull("Bob should have started verif transaction", bobKeyTx)
assertTrue(bobKeyTx is SASDefaultVerificationTransaction)
assertNotNull("Bob should have starting a SAS transaction", bobKeyTx)
assertTrue(aliceKeyTx is SASDefaultVerificationTransaction)
assertEquals("Alice and Bob have same transaction id", aliceKeyTx!!.transactionId, bobKeyTx!!.transactionId)
val aliceSasTx = aliceKeyTx as SASDefaultVerificationTransaction?
val bobSasTx = bobKeyTx as SASDefaultVerificationTransaction?
assertEquals("Alice state should be started", VerificationTxState.Started, aliceSasTx!!.state)
assertEquals("Bob state should be started by alice", VerificationTxState.OnStarted, bobSasTx!!.state)
// Let's cancel from alice side
val cancelLatch = CountDownLatch(1)
val bobListener2 = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.transactionId == txID) {
val immutableState = (tx as SASDefaultVerificationTransaction).state
if (immutableState is VerificationTxState.Cancelled && !immutableState.byMe) {
cancelLatch.countDown()
}
}
}
}
bobVerificationService.addListener(bobListener2)
aliceSasTx.cancel(CancelCode.User)
mTestHelper.await(cancelLatch)
assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled)
assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled)
val aliceCancelState = aliceSasTx.state as VerificationTxState.Cancelled
val bobCancelState = bobSasTx.state as VerificationTxState.Cancelled
assertTrue("Should be cancelled by me on alice side", aliceCancelState.byMe)
assertFalse("Should be cancelled by other on bob side", bobCancelState.byMe)
assertEquals("Should be User cancelled on alice side", CancelCode.User, aliceCancelState.cancelCode)
assertEquals("Should be User cancelled on bob side", CancelCode.User, bobCancelState.cancelCode)
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
cryptoTestData.cleanUp(mTestHelper)
}
@Test
fun test_key_agreement_protocols_must_include_curve25519() {
fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
val protocols = listOf("meh_dont_know")
val tid = "00000000"
// Bob should receive a cancel
var cancelReason: CancelCode? = null
val cancelLatch = CountDownLatch(1)
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.transactionId == tid && tx.state is VerificationTxState.Cancelled) {
cancelReason = (tx.state as VerificationTxState.Cancelled).cancelCode
cancelLatch.countDown()
}
}
}
bobSession.cryptoService().verificationService().addListener(bobListener)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
// TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) {
// TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
// TODO canceledToDeviceEvent = event
// TODO cancelLatch.countDown()
// TODO }
// TODO }
// TODO }
// TODO })
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) {
(tx as IncomingSasVerificationTransaction).performAccept()
}
}
}
aliceSession.cryptoService().verificationService().addListener(aliceListener)
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
mTestHelper.await(cancelLatch)
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
cryptoTestData.cleanUp(mTestHelper)
}
@Test
fun test_key_agreement_macs_Must_include_hmac_sha256() {
fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
val mac = listOf("shaBit")
val tid = "00000000"
// Bob should receive a cancel
var canceledToDeviceEvent: Event? = null
val cancelLatch = CountDownLatch(1)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
// TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) {
// TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
// TODO canceledToDeviceEvent = event
// TODO cancelLatch.countDown()
// TODO }
// TODO }
// TODO }
// TODO })
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
mTestHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.cleanUp(mTestHelper)
}
@Test
fun test_key_agreement_short_code_include_decimal() {
fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
val codes = listOf("bin", "foo", "bar")
val tid = "00000000"
// Bob should receive a cancel
var canceledToDeviceEvent: Event? = null
val cancelLatch = CountDownLatch(1)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
// TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) {
// TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
// TODO canceledToDeviceEvent = event
// TODO cancelLatch.countDown()
// TODO }
// TODO }
// TODO }
// TODO })
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
mTestHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.cleanUp(mTestHelper)
}
private fun fakeBobStart(bobSession: Session,
aliceUserID: String?,
aliceDevice: String?,
tid: String,
protocols: List<String> = SASDefaultVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS,
hashes: List<String> = SASDefaultVerificationTransaction.KNOWN_HASHES,
mac: List<String> = SASDefaultVerificationTransaction.KNOWN_MACS,
codes: List<String> = SASDefaultVerificationTransaction.KNOWN_SHORT_CODES) {
val startMessage = KeyVerificationStart(
fromDevice = bobSession.cryptoService().getMyDevice().deviceId,
method = VerificationMethod.SAS.toValue(),
transactionID = tid,
keyAgreementProtocols = protocols,
hashes = hashes,
messageAuthenticationCodes = mac,
shortAuthenticationStrings = codes
)
val contentMap = MXUsersDevicesMap<Any>()
contentMap.setObject(aliceUserID, aliceDevice, startMessage)
// TODO val sendLatch = CountDownLatch(1)
// TODO bobSession.cryptoRestClient.sendToDevice(
// TODO EventType.KEY_VERIFICATION_START,
// TODO contentMap,
// TODO tid,
// TODO TestMatrixCallback<Void>(sendLatch)
// TODO )
}
// any two devices may only have at most one key verification in flight at a time.
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
@Test
fun test_aliceStartTwoRequests() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val aliceCreatedLatch = CountDownLatch(2)
val aliceCancelledLatch = CountDownLatch(2)
val createdTx = mutableListOf<SASDefaultVerificationTransaction>()
val aliceListener = object : VerificationService.Listener {
override fun transactionCreated(tx: VerificationTransaction) {
createdTx.add(tx as SASDefaultVerificationTransaction)
aliceCreatedLatch.countDown()
}
override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as SASDefaultVerificationTransaction).state is VerificationTxState.Cancelled && !(tx.state as VerificationTxState.Cancelled).byMe) {
aliceCancelledLatch.countDown()
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobUserId = bobSession!!.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceCreatedLatch)
mTestHelper.await(aliceCancelledLatch)
cryptoTestData.cleanUp(mTestHelper)
}
/**
* Test that when alice starts a 'correct' request, bob agrees.
*/
@Test
fun test_aliceAndBobAgreement() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
var accepted: KeyVerificationAccept? = null
var startReq: KeyVerificationStart? = null
val aliceAcceptedLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) {
val at = tx as SASDefaultVerificationTransaction
accepted = at.accepted as? KeyVerificationAccept
startReq = at.startReq as? KeyVerificationStart
aliceAcceptedLatch.countDown()
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) {
val at = tx as IncomingSasVerificationTransaction
at.performAccept()
}
}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceAcceptedLatch)
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false)
// check that agreement is valid
assertTrue("Agreed Protocol should be Valid", accepted!!.isValid())
assertTrue("Agreed Protocol should be known by alice", startReq!!.keyAgreementProtocols!!.contains(accepted!!.keyAgreementProtocol))
assertTrue("Hash should be known by alice", startReq!!.hashes!!.contains(accepted!!.hash))
assertTrue("Hash should be known by alice", startReq!!.messageAuthenticationCodes!!.contains(accepted!!.messageAuthenticationCode))
accepted!!.shortAuthenticationStrings?.forEach {
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings!!.contains(it))
}
cryptoTestData.cleanUp(mTestHelper)
}
@Test
fun test_aliceAndBobSASCode() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
when (uxState) {
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
aliceSASLatch.countDown()
}
else -> Unit
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobSASLatch = CountDownLatch(1)
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
tx.performAccept()
}
else -> Unit
}
if (uxState === IncomingSasVerificationTransaction.UxState.SHOW_SAS) {
bobSASLatch.countDown()
}
}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceSASLatch)
mTestHelper.await(bobSASLatch)
val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction
val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction
assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
cryptoTestData.cleanUp(mTestHelper)
}
@Test
fun test_happyPath() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
when (uxState) {
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
tx.userHasVerifiedShortCode()
}
OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
aliceSASLatch.countDown()
}
else -> Unit
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobSASLatch = CountDownLatch(1)
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
tx.performAccept()
}
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
tx.userHasVerifiedShortCode()
}
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
bobSASLatch.countDown()
}
else -> Unit
}
}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceSASLatch)
mTestHelper.await(bobSASLatch)
// Assert that devices are verified
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId)
val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().deviceId)
// latch wait a bit again
Thread.sleep(1000)
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
cryptoTestData.cleanUp(mTestHelper)
}
}

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.verification.qrcode
fun hexToByteArray(hex: String): ByteArray {
// Remove all spaces
return hex.replace(" ", "")
.let {
if (it.length % 2 != 0) "0$it" else it
}
.let {
ByteArray(it.length / 2)
.apply {
for (i in this.indices) {
val index = i * 2
val v = it.substring(index, index + 2).toInt(16)
this[i] = v.toByte()
}
}
}
}

View File

@@ -0,0 +1,249 @@
/*
* 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.verification.qrcode
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import org.amshove.kluent.shouldBeNull
import org.amshove.kluent.shouldEqual
import org.amshove.kluent.shouldEqualTo
import org.amshove.kluent.shouldNotBeNull
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class QrCodeTest : InstrumentedTest {
private val qrCode1 = QrCodeData.VerifyingAnotherUser(
transactionId = "MaTransaction",
userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU",
otherUserMasterCrossSigningPublicKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU",
sharedSecret = "MTIzNDU2Nzg"
)
private val value1 = "MATRIX\u0002\u0000\u0000\u000DMaTransaction\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢UMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥12345678"
private val qrCode2 = QrCodeData.SelfVerifyingMasterKeyTrusted(
transactionId = "MaTransaction",
userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU",
otherDeviceKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU",
sharedSecret = "MTIzNDU2Nzg"
)
private val value2 = "MATRIX\u0002\u0001\u0000\u000DMaTransaction\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢UMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥12345678"
private val qrCode3 = QrCodeData.SelfVerifyingMasterKeyNotTrusted(
transactionId = "MaTransaction",
deviceKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU",
userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU",
sharedSecret = "MTIzNDU2Nzg"
)
private val value3 = "MATRIX\u0002\u0002\u0000\u000DMaTransactionMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008B\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢U12345678"
private val sharedSecretByteArray = "12345678".toByteArray(Charsets.ISO_8859_1)
private val tlx_byteArray = hexToByteArray("4d 79 6e 64 a4 d9 2e f4 91 58 e4 cf 94 ea 8b ab 9d f8 6c 0f bf 2b 8c cb 14 a4 ae f5 c1 8b 41 a5")
private val kte_byteArray = hexToByteArray("92 d1 30 71 43 fa b2 ed 71 87 e1 ae 13 e0 98 91 0d c7 e9 6f c3 22 5f b2 6c 71 5d 68 43 ab a2 55")
@Test
fun testEncoding1() {
qrCode1.toEncodedString() shouldEqual value1
}
@Test
fun testEncoding2() {
qrCode2.toEncodedString() shouldEqual value2
}
@Test
fun testEncoding3() {
qrCode3.toEncodedString() shouldEqual value3
}
@Test
fun testSymmetry1() {
qrCode1.toEncodedString().toQrCodeData() shouldEqual qrCode1
}
@Test
fun testSymmetry2() {
qrCode2.toEncodedString().toQrCodeData() shouldEqual qrCode2
}
@Test
fun testSymmetry3() {
qrCode3.toEncodedString().toQrCodeData() shouldEqual qrCode3
}
@Test
fun testCase1() {
val url = qrCode1.toEncodedString()
val byteArray = url.toByteArray(Charsets.ISO_8859_1)
checkHeader(byteArray)
// Mode
byteArray[7] shouldEqualTo 0
checkSizeAndTransaction(byteArray)
compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
compareArray(byteArray.copyOfRange(23 + 32, 23 + 64), tlx_byteArray)
compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray)
}
@Test
fun testCase2() {
val url = qrCode2.toEncodedString()
val byteArray = url.toByteArray(Charsets.ISO_8859_1)
checkHeader(byteArray)
// Mode
byteArray[7] shouldEqualTo 1
checkSizeAndTransaction(byteArray)
compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
compareArray(byteArray.copyOfRange(23 + 32, 23 + 64), tlx_byteArray)
compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray)
}
@Test
fun testCase3() {
val url = qrCode3.toEncodedString()
val byteArray = url.toByteArray(Charsets.ISO_8859_1)
checkHeader(byteArray)
// Mode
byteArray[7] shouldEqualTo 2
checkSizeAndTransaction(byteArray)
compareArray(byteArray.copyOfRange(23, 23 + 32), tlx_byteArray)
compareArray(byteArray.copyOfRange(23 + 32, 23 + 64), kte_byteArray)
compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray)
}
@Test
fun testLongTransactionId() {
// Size on two bytes (2_000 = 0x07D0)
val longTransactionId = "PatternId_".repeat(200)
val qrCode = qrCode1.copy(transactionId = longTransactionId)
val result = qrCode.toEncodedString()
val expected = value1.replace("\u0000\u000DMaTransaction", "\u0007\u00D0$longTransactionId")
result shouldEqual expected
// Reverse operation
expected.toQrCodeData() shouldEqual qrCode
}
@Test
fun testAnyTransactionId() {
for (qty in 0 until 0x1FFF step 200) {
val longTransactionId = "a".repeat(qty)
val qrCode = qrCode1.copy(transactionId = longTransactionId)
// Symmetric operation
qrCode.toEncodedString().toQrCodeData() shouldEqual qrCode
}
}
// Error cases
@Test
fun testErrorHeader() {
value1.replace("MATRIX", "MOTRIX").toQrCodeData().shouldBeNull()
value1.replace("MATRIX", "MATRI").toQrCodeData().shouldBeNull()
value1.replace("MATRIX", "").toQrCodeData().shouldBeNull()
}
@Test
fun testErrorVersion() {
value1.replace("MATRIX\u0002", "MATRIX\u0000").toQrCodeData().shouldBeNull()
value1.replace("MATRIX\u0002", "MATRIX\u0001").toQrCodeData().shouldBeNull()
value1.replace("MATRIX\u0002", "MATRIX\u0003").toQrCodeData().shouldBeNull()
value1.replace("MATRIX\u0002", "MATRIX").toQrCodeData().shouldBeNull()
}
@Test
fun testErrorSecretTooShort() {
value1.replace("12345678", "1234567").toQrCodeData().shouldBeNull()
}
@Test
fun testErrorNoTransactionNoKeyNoSecret() {
// But keep transaction length
"MATRIX\u0002\u0000\u0000\u000D".toQrCodeData().shouldBeNull()
}
@Test
fun testErrorNoKeyNoSecret() {
"MATRIX\u0002\u0000\u0000\u000DMaTransaction".toQrCodeData().shouldBeNull()
}
@Test
fun testErrorTransactionLengthTooShort() {
// In this case, the secret will be longer, so this is not an error, but it will lead to keys mismatch
value1.replace("\u000DMaTransaction", "\u000CMaTransaction").toQrCodeData().shouldNotBeNull()
}
@Test
fun testErrorTransactionLengthTooBig() {
value1.replace("\u000DMaTransaction", "\u000EMaTransaction").toQrCodeData().shouldBeNull()
}
private fun compareArray(actual: ByteArray, expected: ByteArray) {
actual.size shouldEqual expected.size
for (i in actual.indices) {
actual[i] shouldEqualTo expected[i]
}
}
private fun checkHeader(byteArray: ByteArray) {
// MATRIX
byteArray[0] shouldEqualTo 'M'.toByte()
byteArray[1] shouldEqualTo 'A'.toByte()
byteArray[2] shouldEqualTo 'T'.toByte()
byteArray[3] shouldEqualTo 'R'.toByte()
byteArray[4] shouldEqualTo 'I'.toByte()
byteArray[5] shouldEqualTo 'X'.toByte()
// Version
byteArray[6] shouldEqualTo 2
}
private fun checkSizeAndTransaction(byteArray: ByteArray) {
// Size
byteArray[8] shouldEqualTo 0
byteArray[9] shouldEqualTo 13
// Transaction
byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldEqual "MaTransaction"
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.verification.qrcode
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import org.amshove.kluent.shouldBe
import org.amshove.kluent.shouldNotBeEqualTo
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class SharedSecretTest : InstrumentedTest {
@Test
fun testSharedSecretLengthCase() {
repeat(100) {
generateSharedSecretV2().length shouldBe 11
}
}
@Test
fun testSharedDiffCase() {
val sharedSecret1 = generateSharedSecretV2()
val sharedSecret2 = generateSharedSecretV2()
sharedSecret1 shouldNotBeEqualTo sharedSecret2
}
}

View File

@@ -0,0 +1,232 @@
/*
* 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.verification.qrcode
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
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.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.TestConstants
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
import org.amshove.kluent.shouldBe
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 VerificationTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
data class ExpectedResult(
val sasIsSupported: Boolean = false,
val otherCanScanQrCode: Boolean = false,
val otherCanShowQrCode: Boolean = false
)
private val sas = listOf(
VerificationMethod.SAS
)
private val sasShow = listOf(
VerificationMethod.SAS,
VerificationMethod.QR_CODE_SHOW
)
private val sasScan = listOf(
VerificationMethod.SAS,
VerificationMethod.QR_CODE_SCAN
)
private val sasShowScan = listOf(
VerificationMethod.SAS,
VerificationMethod.QR_CODE_SHOW,
VerificationMethod.QR_CODE_SCAN
)
@Test
fun test_aliceAndBob_sas_sas() = doTest(
sas,
sas,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_sas_show() = doTest(
sas,
sasShow,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_show_sas() = doTest(
sasShow,
sas,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_sas_scan() = doTest(
sas,
sasScan,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_scan_sas() = doTest(
sasScan,
sas,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_scan_scan() = doTest(
sasScan,
sasScan,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_show_show() = doTest(
sasShow,
sasShow,
ExpectedResult(sasIsSupported = true),
ExpectedResult(sasIsSupported = true)
)
@Test
fun test_aliceAndBob_show_scan() = doTest(
sasShow,
sasScan,
ExpectedResult(sasIsSupported = true, otherCanScanQrCode = true),
ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true)
)
@Test
fun test_aliceAndBob_scan_show() = doTest(
sasScan,
sasShow,
ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true),
ExpectedResult(sasIsSupported = true, otherCanScanQrCode = true)
)
@Test
fun test_aliceAndBob_all_all() = doTest(
sasShowScan,
sasShowScan,
ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true, otherCanScanQrCode = true),
ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true, otherCanScanQrCode = true)
)
// TODO Add tests without SAS
private fun doTest(aliceSupportedMethods: List<VerificationMethod>,
bobSupportedMethods: List<VerificationMethod>,
expectedResultForAlice: ExpectedResult,
expectedResultForBob: ExpectedResult) {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession!!
mTestHelper.doSync<Unit> { callback ->
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
), callback)
}
mTestHelper.doSync<Unit> { callback ->
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = bobSession.myUserId,
password = TestConstants.PASSWORD
), callback)
}
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession.cryptoService().verificationService()
var aliceReadyPendingVerificationRequest: PendingVerificationRequest? = null
var bobReadyPendingVerificationRequest: PendingVerificationRequest? = null
val latch = CountDownLatch(2)
val aliceListener = object : VerificationService.Listener {
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
// Step 4: Alice receive the ready request
if (pr.isReady) {
aliceReadyPendingVerificationRequest = pr
latch.countDown()
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobListener = object : VerificationService.Listener {
override fun verificationRequestCreated(pr: PendingVerificationRequest) {
// Step 2: Bob accepts the verification request
bobVerificationService.readyPendingVerificationInDMs(
bobSupportedMethods,
aliceSession.myUserId,
cryptoTestData.roomId,
pr.transactionId!!
)
}
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
// Step 3: Bob is ready
if (pr.isReady) {
bobReadyPendingVerificationRequest = pr
latch.countDown()
}
}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
// Step 1: Alice starts a verification request
aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId)
mTestHelper.await(latch)
aliceReadyPendingVerificationRequest!!.let { pr ->
pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported
pr.otherCanShowQrCode() shouldBe expectedResultForAlice.otherCanShowQrCode
pr.otherCanScanQrCode() shouldBe expectedResultForAlice.otherCanScanQrCode
}
bobReadyPendingVerificationRequest!!.let { pr ->
pr.isSasSupported() shouldBe expectedResultForBob.sasIsSupported
pr.otherCanShowQrCode() shouldBe expectedResultForBob.otherCanShowQrCode
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
}
cryptoTestData.cleanUp(mTestHelper)
}
}

View File

@@ -19,16 +19,19 @@ package im.vector.matrix.android.session.room.timeline
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.internal.database.helper.*
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.helper.addTimelineEvent
import im.vector.matrix.android.internal.database.helper.merge
import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.SessionRealmModule
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents
import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeMessageEvent
import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeRoomMemberEvent
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.createObject
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue
import org.amshove.kluent.shouldEqual
import org.junit.Before
@@ -43,7 +46,11 @@ internal class ChunkEntityTest : InstrumentedTest {
@Before
fun setup() {
Realm.init(context())
val testConfig = RealmConfiguration.Builder().inMemory().name("test-realm").build()
val testConfig = RealmConfiguration.Builder()
.inMemory()
.name("test-realm")
.modules(SessionRealmModule())
.build()
monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
}
@@ -51,8 +58,11 @@ internal class ChunkEntityTest : InstrumentedTest {
fun add_shouldAdd_whenNotAlreadyIncluded() {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvent = createFakeMessageEvent()
chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED).let {
realm.copyToRealmOrUpdate(it)
}
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
chunk.timelineEvents.size shouldEqual 1
}
}
@@ -61,65 +71,23 @@ internal class ChunkEntityTest : InstrumentedTest {
fun add_shouldNotAdd_whenAlreadyIncluded() {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvent = createFakeMessageEvent()
chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED).let {
realm.copyToRealmOrUpdate(it)
}
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap())
chunk.timelineEvents.size shouldEqual 1
}
}
@Test
fun add_shouldStateIndexIncremented_whenStateEventIsAddedForward() {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvent = createFakeRoomMemberEvent()
chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 1
}
}
@Test
fun add_shouldStateIndexNotIncremented_whenNoStateEventIsAdded() {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvent = createFakeMessageEvent()
chunk.add("roomId", fakeEvent, PaginationDirection.FORWARDS)
chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 0
}
}
@Test
fun addAll_shouldStateIndexIncremented_whenStateEventsAreAddedForward() {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvents = createFakeListOfEvents(30)
val numberOfStateEvents = fakeEvents.filter { it.isStateEvent() }.size
chunk.addAll("roomId", fakeEvents, PaginationDirection.FORWARDS)
chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual numberOfStateEvents
}
}
@Test
fun addAll_shouldStateIndexDecremented_whenStateEventsAreAddedBackward() {
monarchy.runTransactionSync { realm ->
val chunk: ChunkEntity = realm.createObject()
val fakeEvents = createFakeListOfEvents(30)
val numberOfStateEvents = fakeEvents.filter { it.isStateEvent() }.size
val lastIsState = fakeEvents.last().isStateEvent()
val expectedStateIndex = if (lastIsState) -numberOfStateEvents + 1 else -numberOfStateEvents
chunk.addAll("roomId", fakeEvents, PaginationDirection.BACKWARDS)
chunk.lastStateIndex(PaginationDirection.BACKWARDS) shouldEqual expectedStateIndex
}
}
@Test
fun merge_shouldAddEvents_whenMergingBackward() {
monarchy.runTransactionSync { realm ->
val chunk1: ChunkEntity = realm.createObject()
val chunk2: ChunkEntity = realm.createObject()
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
chunk1.timelineEvents.size shouldEqual 60
}
}
@@ -133,38 +101,14 @@ internal class ChunkEntityTest : InstrumentedTest {
val eventsForChunk2 = eventsForChunk1 + createFakeListOfEvents(10)
chunk1.isLastForward = true
chunk2.isLastForward = false
chunk1.addAll("roomId", eventsForChunk1, PaginationDirection.FORWARDS)
chunk2.addAll("roomId", eventsForChunk2, PaginationDirection.BACKWARDS)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.addAll(ROOM_ID, eventsForChunk1, PaginationDirection.FORWARDS)
chunk2.addAll(ROOM_ID, eventsForChunk2, PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
chunk1.timelineEvents.size shouldEqual 40
chunk1.isLastForward.shouldBeTrue()
}
}
@Test
fun merge_shouldEventsBeLinked_whenMergingLinkedWithUnlinked() {
monarchy.runTransactionSync { realm ->
val chunk1: ChunkEntity = realm.createObject()
val chunk2: ChunkEntity = realm.createObject()
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = false)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.isUnlinked().shouldBeFalse()
}
}
@Test
fun merge_shouldEventsBeUnlinked_whenMergingUnlinkedWithUnlinked() {
monarchy.runTransactionSync { realm ->
val chunk1: ChunkEntity = realm.createObject()
val chunk2: ChunkEntity = realm.createObject()
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.isUnlinked().shouldBeTrue()
}
}
@Test
fun merge_shouldPrevTokenMerged_whenMergingForwards() {
monarchy.runTransactionSync { realm ->
@@ -172,9 +116,9 @@ internal class ChunkEntityTest : InstrumentedTest {
val chunk2: ChunkEntity = realm.createObject()
val prevToken = "prev_token"
chunk1.prevToken = prevToken
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk1.merge("roomId", chunk2, PaginationDirection.FORWARDS)
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.FORWARDS)
chunk1.prevToken shouldEqual prevToken
}
}
@@ -186,10 +130,25 @@ internal class ChunkEntityTest : InstrumentedTest {
val chunk2: ChunkEntity = realm.createObject()
val nextToken = "next_token"
chunk1.nextToken = nextToken
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS)
chunk1.nextToken shouldEqual nextToken
}
}
private fun ChunkEntity.addAll(roomId: String,
events: List<Event>,
direction: PaginationDirection) {
events.forEach { event ->
val fakeEvent = event.toEntity(roomId, SendState.SYNCED).let {
realm.copyToRealmOrUpdate(it)
}
addTimelineEvent(roomId, fakeEvent, direction, emptyMap())
}
}
companion object {
private const val ROOM_ID = "roomId"
}
}

View File

@@ -16,21 +16,14 @@
package im.vector.matrix.android.session.room.timeline
import com.zhuinden.monarchy.Monarchy
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.EventType
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.internal.database.helper.addAll
import im.vector.matrix.android.internal.database.helper.addOrUpdate
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
import io.realm.kotlin.createObject
import kotlin.random.Random
object RoomDataHelper {
@@ -70,22 +63,7 @@ object RoomDataHelper {
}
fun createFakeRoomMemberEvent(): Event {
val roomMember = RoomMember(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
val roomMember = RoomMemberSummary(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
}
fun fakeInitialSync(monarchy: Monarchy, roomId: String) {
monarchy.runTransactionSync { realm ->
val roomEntity = realm.createObject<RoomEntity>(roomId)
roomEntity.membership = Membership.JOIN
val eventList = createFakeListOfEvents(10)
val chunkEntity = realm.createObject<ChunkEntity>().apply {
nextToken = null
prevToken = Random.nextLong(System.currentTimeMillis()).toString()
isLastForward = true
}
chunkEntity.addAll(roomId, eventList, PaginationDirection.FORWARDS)
roomEntity.addOrUpdate(chunkEntity)
}
}
}

View File

@@ -66,7 +66,7 @@ internal class TimelineTest : InstrumentedTest {
// val latch = CountDownLatch(2)
// var timelineEvents: List<TimelineEvent> = emptyList()
// timeline.listener = object : Timeline.Listener {
// override fun onUpdated(snapshot: List<TimelineEvent>) {
// override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
// if (snapshot.isNotEmpty()) {
// if (initialLoad == 0) {
// initialLoad = snapshot.size

View File

@@ -6,9 +6,10 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<application android:networkSecurityConfig="@xml/network_security_config">
<provider android:name="androidx.work.impl.WorkManagerInitializer"
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />

View File

@@ -23,16 +23,21 @@ import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.BuildConfig
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.crypto.attachments.ElementToDecrypt
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
import im.vector.matrix.android.internal.di.DaggerMatrixComponent
import im.vector.matrix.android.internal.network.UserAgentHolder
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import org.matrix.olm.OlmManager
import java.io.InputStream
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
data class MatrixConfiguration(
val applicationFlavor: String = "Default-application-flavor"
val applicationFlavor: String = "Default-application-flavor",
val cryptoConfig: MXCryptoConfig = MXCryptoConfig()
) {
interface Provider {
@@ -54,12 +59,11 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
init {
Monarchy.init(context)
DaggerMatrixComponent.factory().create(context).inject(this)
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) {
WorkManager.initialize(context, Configuration.Builder().build())
}
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
userAgentHolder.setApplicationFlavor(matrixConfiguration.applicationFlavor)
}
fun getUserAgent() = userAgentHolder.userAgent
@@ -96,5 +100,9 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
fun getSdkVersion(): String {
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
}
fun decryptStream(inputStream: InputStream?, elementToDecrypt: ElementToDecrypt): InputStream? {
return MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt)
}
}
}

View File

@@ -38,3 +38,8 @@ interface MatrixCallback<in T> {
// no-op
}
}
/**
* Basic no op implementation
*/
class NoOpMatrixCallback<T>: MatrixCallback<T>

View File

@@ -122,9 +122,9 @@ object MatrixPatterns {
*/
fun isEventId(str: String?): Boolean {
return str != null
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
}
/**
@@ -144,14 +144,6 @@ object MatrixPatterns {
* @return null if not found or if matrixId is null
*/
fun extractServerNameFromId(matrixId: String?): String? {
if (matrixId == null) {
return null
}
val index = matrixId.indexOf(":")
return if (index == -1) {
null
} else matrixId.substring(index + 1)
return matrixId?.substringAfter(":", missingDelimiterValue = "")?.takeIf { it.isNotEmpty() }
}
}

View File

@@ -18,6 +18,7 @@ package im.vector.matrix.android.api.auth.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.util.md5
/**
* This data class hold credentials user data.
@@ -34,3 +35,7 @@ data class Credentials(
// Optional data that may contain info to override home server and/or identity server
@Json(name = "well_known") val wellKnown: WellKnown? = null
)
internal fun Credentials.sessionId(): String {
return (if (deviceId.isNullOrBlank()) userId else "$userId|$deviceId").md5()
}

View File

@@ -22,5 +22,6 @@ package im.vector.matrix.android.api.auth.data
*/
data class SessionParams(
val credentials: Credentials,
val homeServerConnectionConfig: HomeServerConnectionConfig
val homeServerConnectionConfig: HomeServerConnectionConfig,
val isTokenValid: Boolean
)

View File

@@ -46,13 +46,13 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class WellKnown(
@Json(name = "m.homeserver")
var homeServer: WellKnownBaseConfig? = null,
val homeServer: WellKnownBaseConfig? = null,
@Json(name = "m.identity_server")
var identityServer: WellKnownBaseConfig? = null,
val identityServer: WellKnownBaseConfig? = null,
@Json(name = "m.integrations")
var integrations: Map<String, @JvmSuppressWildcards Any>? = null
val integrations: Map<String, @JvmSuppressWildcards Any>? = null
) {
/**
* Returns the list of integration managers proposed

View File

@@ -16,7 +16,7 @@
package im.vector.matrix.android.api.crypto
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
import im.vector.matrix.android.internal.crypto.verification.getEmojiForCode
/**

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto
package im.vector.matrix.android.api.crypto
/**
* Class to define the parameters used to customize or configure the end-to-end crypto.
*/
data class MXCryptoConfig(
// Tell whether the encryption of the event content is enabled for the invited members.
// By default, we encrypt messages only for the joined members.
// The encryption for the invited members will be blocked if the history visibility is "joined".
var enableEncryptionForInvitedMembers: Boolean = 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".
var enableEncryptionForInvitedMembers: Boolean = true
)

View File

@@ -0,0 +1,31 @@
/*
* Copyright 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.crypto
/**
* RoomEncryptionTrustLevel represents the trust level in an encrypted room.
*/
enum class RoomEncryptionTrustLevel {
// No one in the room has been verified -> Black shield
Default,
// There are one or more device un-verified -> the app should display a red shield
Warning,
// All devices in the room are verified -> the app should display a green shield
Trusted
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 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
fun Boolean?.orTrue() = this ?: true
fun Boolean?.orFalse() = this ?: false

View File

@@ -16,18 +16,21 @@
package im.vector.matrix.android.api.extensions
import im.vector.matrix.android.api.comparators.DatedObjectComparators
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
/* ==========================================================================================
* MXDeviceInfo
* ========================================================================================== */
fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint()
fun CryptoDeviceInfo.getFingerprintHumanReadable() = fingerprint()
?.chunked(4)
?.joinToString(separator = " ")
fun MutableList<DeviceInfo>.sortByLastSeen() {
sortWith(DatedObjectComparators.descComparator)
/* ==========================================================================================
* DeviceInfo
* ========================================================================================== */
fun List<DeviceInfo>.sortByLastSeen(): List<DeviceInfo> {
return this.sortedByDescending { it.lastSeenTs ?: 0 }
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2019 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.failure
import javax.net.ssl.HttpsURLConnection
fun Throwable.is401() =
this is Failure.ServerError
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
&& error.code == MatrixError.M_UNAUTHORIZED
fun Throwable.isTokenError() =
this is Failure.ServerError
&& (error.code == MatrixError.M_UNKNOWN_TOKEN || error.code == MatrixError.M_MISSING_TOKEN)
fun Throwable.shouldBeRetried(): Boolean {
return this is Failure.NetworkConnection
|| (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED)
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2019 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.failure
// This class will be sent to the bus
sealed class GlobalError {
data class InvalidToken(val softLogout: Boolean) : GlobalError()
data class ConsentNotGivenError(val consentUri: String) : GlobalError()
}

View File

@@ -22,45 +22,112 @@ import com.squareup.moshi.JsonClass
/**
* This data class holds the error defined by the matrix specifications.
* You shouldn't have to instantiate it.
* Ref: https://matrix.org/docs/spec/client_server/latest#api-standards
*/
@JsonClass(generateAdapter = true)
data class MatrixError(
/** unique string which can be used to handle an error message */
@Json(name = "errcode") val code: String,
/** human-readable error message */
@Json(name = "error") val message: String,
// For M_CONSENT_NOT_GIVEN
@Json(name = "consent_uri") val consentUri: String? = null,
// RESOURCE_LIMIT_EXCEEDED data
// For M_RESOURCE_LIMIT_EXCEEDED
@Json(name = "limit_type") val limitType: String? = null,
@Json(name = "admin_contact") val adminUri: String? = null,
// For LIMIT_EXCEEDED
@Json(name = "retry_after_ms") val retryAfterMillis: Long? = null) {
// For M_LIMIT_EXCEEDED
@Json(name = "retry_after_ms") val retryAfterMillis: Long? = null,
// For M_UNKNOWN_TOKEN
@Json(name = "soft_logout") val isSoftLogout: Boolean = false
) {
companion object {
const val FORBIDDEN = "M_FORBIDDEN"
const val UNKNOWN = "M_UNKNOWN"
const val UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
const val MISSING_TOKEN = "M_MISSING_TOKEN"
const val BAD_JSON = "M_BAD_JSON"
const val NOT_JSON = "M_NOT_JSON"
const val NOT_FOUND = "M_NOT_FOUND"
const val LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
const val USER_IN_USE = "M_USER_IN_USE"
const val ROOM_IN_USE = "M_ROOM_IN_USE"
const val BAD_PAGINATION = "M_BAD_PAGINATION"
const val UNAUTHORIZED = "M_UNAUTHORIZED"
const val OLD_VERSION = "M_OLD_VERSION"
const val UNRECOGNIZED = "M_UNRECOGNIZED"
/** Forbidden access, e.g. joining a room without permission, failed login. */
const val M_FORBIDDEN = "M_FORBIDDEN"
/** An unknown error has occurred. */
const val M_UNKNOWN = "M_UNKNOWN"
/** The access token specified was not recognised. */
const val M_UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
/** No access token was specified for the request. */
const val M_MISSING_TOKEN = "M_MISSING_TOKEN"
/** Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys. */
const val M_BAD_JSON = "M_BAD_JSON"
/** Request did not contain valid JSON. */
const val M_NOT_JSON = "M_NOT_JSON"
/** No resource was found for this request. */
const val M_NOT_FOUND = "M_NOT_FOUND"
/** Too many requests have been sent in a short period of time. Wait a while then try again. */
const val M_LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
const val LOGIN_EMAIL_URL_NOT_YET = "M_LOGIN_EMAIL_URL_NOT_YET"
const val THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
// Error code returned by the server when no account matches the given 3pid
const val THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
const val THREEPID_IN_USE = "M_THREEPID_IN_USE"
const val SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
const val TOO_LARGE = "M_TOO_LARGE"
/* ==========================================================================================
* Other error codes the client might encounter are
* ========================================================================================== */
/** Encountered when trying to register a user ID which has been taken. */
const val M_USER_IN_USE = "M_USER_IN_USE"
/** Sent when the room alias given to the createRoom API is already in use. */
const val M_ROOM_IN_USE = "M_ROOM_IN_USE"
/** (Not documented yet) */
const val M_BAD_PAGINATION = "M_BAD_PAGINATION"
/** The request was not correctly authorized. Usually due to login failures. */
const val M_UNAUTHORIZED = "M_UNAUTHORIZED"
/** (Not documented yet) */
const val M_OLD_VERSION = "M_OLD_VERSION"
/** The server did not understand the request. */
const val M_UNRECOGNIZED = "M_UNRECOGNIZED"
/** (Not documented yet) */
const val M_LOGIN_EMAIL_URL_NOT_YET = "M_LOGIN_EMAIL_URL_NOT_YET"
/** Authentication could not be performed on the third party identifier. */
const val M_THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
/** Sent when a threepid given to an API cannot be used because no record matching the threepid was found. */
const val M_THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
/** Sent when a threepid given to an API cannot be used because the same threepid is already in use. */
const val M_THREEPID_IN_USE = "M_THREEPID_IN_USE"
/** The client's request used a third party server, eg. identity server, that this server does not trust. */
const val M_SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
/** The request or entity was too large. */
const val M_TOO_LARGE = "M_TOO_LARGE"
/** (Not documented yet) */
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
/** The request cannot be completed because the homeserver has reached a resource limit imposed on it. For example,
* a homeserver held in a shared hosting environment may reach a resource limit if it starts using too much memory
* or disk space. The error MUST have an admin_contact field to provide the user receiving the error a place to reach
* out to. Typically, this error will appear on routes which attempt to modify state (eg: sending messages, account
* data, etc) and not routes which only read state (eg: /sync, get account data, etc). */
const val M_RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
/** The user ID associated with the request has been deactivated. Typically for endpoints that prove authentication, such as /login. */
const val M_USER_DEACTIVATED = "M_USER_DEACTIVATED"
/** Encountered when trying to register a user ID which is not valid. */
const val M_INVALID_USERNAME = "M_INVALID_USERNAME"
/** Sent when the initial state given to the createRoom API is invalid. */
const val M_INVALID_ROOM_STATE = "M_INVALID_ROOM_STATE"
/** The server does not permit this third party identifier. This may happen if the server only permits,
* for example, email addresses from a particular domain. */
const val M_THREEPID_DENIED = "M_THREEPID_DENIED"
/** The client's request to create a room used a room version that the server does not support. */
const val M_UNSUPPORTED_ROOM_VERSION = "M_UNSUPPORTED_ROOM_VERSION"
/** The client attempted to join a room that has a version the server does not support.
* Inspect the room_version property of the error response for the room's version. */
const val M_INCOMPATIBLE_ROOM_VERSION = "M_INCOMPATIBLE_ROOM_VERSION"
/** The state change requested cannot be performed, such as attempting to unban a user who is not banned. */
const val M_BAD_STATE = "M_BAD_STATE"
/** The room or resource does not permit guests to access it. */
const val M_GUEST_ACCESS_FORBIDDEN = "M_GUEST_ACCESS_FORBIDDEN"
/** A Captcha is required to complete the request. */
const val M_CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED"
/** The Captcha provided did not match what was expected. */
const val M_CAPTCHA_INVALID = "M_CAPTCHA_INVALID"
/** A required parameter was missing from the request. */
const val M_MISSING_PARAM = "M_MISSING_PARAM"
/** A parameter that was specified has the wrong value. For example, the server expected an integer and instead received a string. */
const val M_INVALID_PARAM = "M_INVALID_PARAM"
/** The resource being requested is reserved by an application service, or the application service making the request has not created the resource. */
const val M_EXCLUSIVE = "M_EXCLUSIVE"
/** The user is unable to reject an invite to join the server notices room. See the Server Notices module for more information. */
const val M_CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
/** (Not documented yet) */
const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
// Possible value for "limit_type"
const val LIMIT_TYPE_MAU = "monthly_active_user"

View File

@@ -21,6 +21,7 @@ package im.vector.matrix.android.api.listeners
*/
interface ProgressListener {
/**
* Will be invoked on the background thread, not in UI thread.
* @param progress from 0 to total by contract
* @param total
*/

View File

@@ -17,7 +17,6 @@
package im.vector.matrix.android.api.permalinks
import android.text.Spannable
import im.vector.matrix.android.api.MatrixPatterns
/**
* MatrixLinkify take a piece of text and turns all of the
@@ -30,7 +29,13 @@ object MatrixLinkify {
*
* @param spannable the text in which the matrix items has to be clickable.
*/
@Suppress("UNUSED_PARAMETER")
fun addLinks(spannable: Spannable, callback: MatrixPermalinkSpan.Callback?): Boolean {
/**
* I disable it because it mess up with pills, and even with pills, it does not work correctly:
* The url is not correct. Ex: for @user:matrix.org, the url will be @user:matrix.org, instead of a matrix.to
*/
/*
// sanity checks
if (spannable.isEmpty()) {
return false
@@ -50,5 +55,7 @@ object MatrixLinkify {
}
}
return hasMatch
*/
return false
}
}

View File

@@ -24,9 +24,7 @@ import android.net.Uri
*/
sealed class PermalinkData {
data class EventLink(val roomIdOrAlias: String, val eventId: String) : PermalinkData()
data class RoomLink(val roomIdOrAlias: String) : PermalinkData()
data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?) : PermalinkData()
data class UserLink(val userId: String) : PermalinkData()

View File

@@ -19,7 +19,7 @@ package im.vector.matrix.android.api.permalinks
import im.vector.matrix.android.api.session.events.model.Event
/**
* Useful methods to create Matrix permalink.
* Useful methods to create Matrix permalink (matrix.to links).
*/
object PermalinkFactory {
@@ -84,7 +84,17 @@ object PermalinkFactory {
* @param id the id to escape
* @return the escaped id
*/
private fun escape(id: String): String {
internal fun escape(id: String): String {
return id.replace("/", "%2F")
}
/**
* Unescape '/' in id
*
* @param id the id to escape
* @return the escaped id
*/
internal fun unescape(id: String): String {
return id.replace("%2F", "/")
}
}

View File

@@ -56,20 +56,25 @@ object PermalinkParser {
val identifier = params.getOrNull(0)
val extraParameter = params.getOrNull(1)
if (identifier.isNullOrEmpty()) {
return PermalinkData.FallbackLink(uri)
}
return when {
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
MatrixPatterns.isRoomId(identifier) -> {
if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) {
PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter)
} else {
PermalinkData.RoomLink(roomIdOrAlias = identifier)
}
identifier.isNullOrEmpty() -> PermalinkData.FallbackLink(uri)
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
MatrixPatterns.isRoomId(identifier) -> {
PermalinkData.RoomLink(
roomIdOrAlias = identifier,
isRoomAlias = false,
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }
)
}
else -> PermalinkData.FallbackLink(uri)
MatrixPatterns.isRoomAlias(identifier) -> {
PermalinkData.RoomLink(
roomIdOrAlias = identifier,
isRoomAlias = true,
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }
)
}
else -> PermalinkData.FallbackLink(uri)
}
}
}

View File

@@ -15,30 +15,32 @@
*/
package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
abstract class Condition(val kind: Kind) {
enum class Kind(val value: String) {
event_match("event_match"),
contains_display_name("contains_display_name"),
room_member_count("room_member_count"),
sender_notification_permission("sender_notification_permission"),
UNRECOGNIZE("");
EventMatch("event_match"),
ContainsDisplayName("contains_display_name"),
RoomMemberCount("room_member_count"),
SenderNotificationPermission("sender_notification_permission"),
Unrecognised("");
companion object {
fun fromString(value: String): Kind {
return when (value) {
"event_match" -> event_match
"contains_display_name" -> contains_display_name
"room_member_count" -> room_member_count
"sender_notification_permission" -> sender_notification_permission
else -> UNRECOGNIZE
"event_match" -> EventMatch
"contains_display_name" -> ContainsDisplayName
"room_member_count" -> RoomMemberCount
"sender_notification_permission" -> SenderNotificationPermission
else -> Unrecognised
}
}
}
}
abstract fun isSatisfied(conditionResolver: ConditionResolver): Boolean
abstract fun isSatisfied(event: Event, conditionResolver: ConditionResolver): Boolean
open fun technicalDescription(): String {
return "Kind: $kind"

View File

@@ -15,14 +15,22 @@
*/
package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
/**
* Acts like a visitor on Conditions.
* This class as all required context needed to evaluate rules
*/
interface ConditionResolver {
fun resolveEventMatchCondition(event: Event,
condition: EventMatchCondition): Boolean
fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean
fun resolveRoomMemberCountCondition(roomMemberCountCondition: RoomMemberCountCondition): Boolean
fun resolveSenderNotificationPermissionCondition(senderNotificationPermissionCondition: SenderNotificationPermissionCondition): Boolean
fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition) : Boolean
fun resolveRoomMemberCountCondition(event: Event,
condition: RoomMemberCountCondition): Boolean
fun resolveSenderNotificationPermissionCondition(event: Event,
condition: SenderNotificationPermissionCondition): Boolean
fun resolveContainsDisplayNameCondition(event: Event,
condition: ContainsDisplayNameCondition): Boolean
}

View File

@@ -21,10 +21,10 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import timber.log.Timber
class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
class ContainsDisplayNameCondition : Condition(Kind.ContainsDisplayName) {
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveContainsDisplayNameCondition(this)
override fun isSatisfied(event: Event, conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveContainsDisplayNameCondition(event, this)
}
override fun technicalDescription(): String {

View File

@@ -19,10 +19,20 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.di.MoshiProvider
import timber.log.Timber
class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind.event_match) {
class EventMatchCondition(
/**
* The dot-separated field of the event to match, e.g. content.body
*/
val key: String,
/**
* The glob-style pattern to match against. Patterns with no special glob characters should
* be treated as having asterisks prepended and appended when testing the condition.
*/
val pattern: String
) : Condition(Kind.EventMatch) {
override fun isSatisfied(conditionResolver: ConditionResolver) : Boolean {
return conditionResolver.resolveEventMatchCondition(this)
override fun isSatisfied(event: Event, conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveEventMatchCondition(event, this)
}
override fun technicalDescription(): String {

View File

@@ -16,25 +16,32 @@
package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.internal.session.room.RoomGetter
import timber.log.Timber
private val regex = Regex("^(==|<=|>=|<|>)?(\\d*)$")
class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_count) {
class RoomMemberCountCondition(
/**
* A decimal integer optionally prefixed by one of ==, <, >, >= or <=.
* A prefix of < matches rooms where the member count is strictly less than the given number and so forth.
* If no prefix is present, this parameter defaults to ==.
*/
val iz: String
) : Condition(Kind.RoomMemberCount) {
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveRoomMemberCountCondition(this)
override fun isSatisfied(event: Event, conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveRoomMemberCountCondition(event, this)
}
override fun technicalDescription(): String {
return "Room member count is $iz"
}
fun isSatisfied(event: Event, session: RoomService?): Boolean {
// sanity check^
internal fun isSatisfied(event: Event, roomGetter: RoomGetter): Boolean {
// sanity checks
val roomId = event.roomId ?: return false
val room = session?.getRoom(roomId) ?: return false
val room = roomGetter.getRoom(roomId) ?: return false
// Parse the is field into prefix and number the first time
val (prefix, count) = parseIsField() ?: return false
@@ -59,7 +66,7 @@ class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_coun
val (prefix, count) = match.destructured
return prefix to count.toInt()
} catch (t: Throwable) {
Timber.d(t)
Timber.e(t, "Unable to parse 'is' field")
}
return null
}

View File

@@ -16,19 +16,29 @@
package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.PowerLevels
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
class SenderNotificationPermissionCondition(
/**
* A string that determines the power level the sender must have to trigger notifications of a given type,
* such as room. Refer to the m.room.power_levels event schema for information about what the defaults are
* and how to interpret the event. The key is used to look up the power level required to send a notification
* type from the notifications object in the power level event content.
*/
val key: String
) : Condition(Kind.SenderNotificationPermission) {
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveSenderNotificationPermissionCondition(this)
override fun isSatisfied(event: Event, conditionResolver: ConditionResolver): Boolean {
return conditionResolver.resolveSenderNotificationPermissionCondition(event, this)
}
override fun technicalDescription(): String {
return "User power level <$key>"
}
fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key)
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
val powerLevelsHelper = PowerLevelsHelper(powerLevels)
return event.senderId != null && powerLevelsHelper.getUserPowerLevel(event.senderId) >= powerLevelsHelper.notificationLevel(key)
}
}

View File

@@ -22,7 +22,7 @@ import com.squareup.moshi.JsonClass
* All push rulesets for a user.
*/
@JsonClass(generateAdapter = true)
data class GetPushRulesResponse(
internal data class GetPushRulesResponse(
/**
* Global rules, account level applying to all devices
*/

View File

@@ -17,7 +17,11 @@ package im.vector.matrix.android.api.pushrules.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.pushrules.*
import im.vector.matrix.android.api.pushrules.Condition
import im.vector.matrix.android.api.pushrules.ContainsDisplayNameCondition
import im.vector.matrix.android.api.pushrules.EventMatchCondition
import im.vector.matrix.android.api.pushrules.RoomMemberCountCondition
import im.vector.matrix.android.api.pushrules.SenderNotificationPermissionCondition
import timber.log.Timber
@JsonClass(generateAdapter = true)
@@ -30,13 +34,13 @@ data class PushCondition(
/**
* Required for event_match conditions. The dot- separated field of the event to match.
*/
val key: String? = null,
/**
*Required for event_match conditions.
*/
/**
* Required for event_match conditions.
*/
val pattern: String? = null,
/**
* Required for room_member_count conditions.
* A decimal integer optionally prefixed by one of, ==, <, >, >= or <=.
@@ -47,30 +51,35 @@ data class PushCondition(
) {
fun asExecutableCondition(): Condition? {
return when (Condition.Kind.fromString(this.kind)) {
Condition.Kind.event_match -> {
if (this.key != null && this.pattern != null) {
return when (Condition.Kind.fromString(kind)) {
Condition.Kind.EventMatch -> {
if (key != null && pattern != null) {
EventMatchCondition(key, pattern)
} else {
Timber.e("Malformed Event match condition")
null
}
}
Condition.Kind.contains_display_name -> {
Condition.Kind.ContainsDisplayName -> {
ContainsDisplayNameCondition()
}
Condition.Kind.room_member_count -> {
if (this.iz.isNullOrBlank()) {
Condition.Kind.RoomMemberCount -> {
if (iz.isNullOrEmpty()) {
Timber.e("Malformed ROOM_MEMBER_COUNT condition")
null
} else {
RoomMemberCountCondition(this.iz)
RoomMemberCountCondition(iz)
}
}
Condition.Kind.sender_notification_permission -> {
this.key?.let { SenderNotificationPermissionCondition(it) }
Condition.Kind.SenderNotificationPermission -> {
if (key == null) {
Timber.e("Malformed Sender Notification Permission condition")
null
} else {
SenderNotificationPermissionCondition(key)
}
}
Condition.Kind.UNRECOGNIZE -> {
Condition.Kind.Unrecognised -> {
Timber.e("Unknown kind $kind")
null
}

View File

@@ -18,7 +18,7 @@ package im.vector.matrix.android.api.pushrules.rest
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class Ruleset(
internal data class Ruleset(
val content: List<PushRule>? = null,
val override: List<PushRule>? = null,
val room: List<PushRule>? = null,

View File

@@ -0,0 +1,35 @@
/*
* Copyright 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.query
/**
* Basic query language. All these cases are mutually exclusive.
*/
sealed class QueryStringValue {
object NoCondition : QueryStringValue()
object IsNull : QueryStringValue()
object IsNotNull : QueryStringValue()
object IsEmpty : QueryStringValue()
object IsNotEmpty : QueryStringValue()
data class Equals(val string: String, val case: Case) : QueryStringValue()
data class Contains(val string: String, val case: Case) : QueryStringValue()
enum class Case {
SENSITIVE,
INSENSITIVE
}
}

View File

@@ -19,8 +19,9 @@ package im.vector.matrix.android.api.session
import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.failure.ConsentNotGivenError
import im.vector.matrix.android.api.failure.GlobalError
import im.vector.matrix.android.api.pushrules.PushRuleService
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.content.ContentUploadStateTracker
import im.vector.matrix.android.api.session.content.ContentUrlResolver
@@ -28,10 +29,12 @@ 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.group.GroupService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
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.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
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.SyncState
@@ -46,28 +49,39 @@ interface Session :
RoomDirectoryService,
GroupService,
UserService,
CryptoService,
CacheService,
SignOutService,
FilterService,
FileService,
ProfileService,
PushRuleService,
PushersService,
InitialSyncProgressService,
HomeServerCapabilitiesService,
SecureStorageService {
SecureStorageService,
AccountDataService {
/**
* The params associated to the session
*/
val sessionParams: SessionParams
/**
* The session is valid, i.e. it has a valid token so far
*/
val isOpenable: Boolean
/**
* Useful shortcut to get access to the userId
*/
val myUserId: String
get() = sessionParams.credentials.userId
/**
* The sessionId
*/
val sessionId: String
/**
* This method allow to open a session. It does start some service on the background.
*/
@@ -81,7 +95,7 @@ interface Session :
/**
* Launches infinite periodic background syncs
* THis does not work in doze mode :/
* This does not work in doze mode :/
* If battery optimization is on it can work in app standby but that's all :/
*/
fun startAutomaticBackgroundSync(repeatDelay: Long = 30_000L)
@@ -102,7 +116,12 @@ interface Session :
* This method allows to listen the sync state.
* @return a [LiveData] of [SyncState].
*/
fun syncState(): LiveData<SyncState>
fun getSyncStateLive(): LiveData<SyncState>
/**
* This methods return true if an initial sync has been processed
*/
fun hasAlreadySynced(): Boolean
/**
* This method allow to close a session. It does stop some services.
@@ -119,6 +138,11 @@ interface Session :
*/
fun contentUploadProgressTracker(): ContentUploadStateTracker
/**
* Returns the cryptoService associated with the session
*/
fun cryptoService(): CryptoService
/**
* Add a listener to the session.
* @param listener the listener to add.
@@ -136,13 +160,12 @@ interface Session :
*/
interface Listener {
/**
* The access token is not valid anymore
* Possible cases:
* - The access token is not valid anymore,
* - a M_CONSENT_NOT_GIVEN error has been received from the homeserver
*/
fun onInvalidToken()
/**
* A M_CONSENT_NOT_GIVEN error has been received from the homeserver
*/
fun onConsentNotGivenError(consentNotGivenError: ConsentNotGivenError)
fun onGlobalError(globalError: GlobalError)
}
val sharedSecretStorageService: SharedSecretStorageService
}

View File

@@ -0,0 +1,53 @@
/*
* 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.accountdata
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
interface AccountDataService {
/**
* Retrieve the account data with the provided type or null if not found
*/
fun getAccountDataEvent(type: String): UserAccountDataEvent?
/**
* Observe the account data with the provided type
*/
fun getLiveAccountDataEvent(type: String): LiveData<Optional<UserAccountDataEvent>>
/**
* Retrieve the account data with the provided types. The return list can have a different size that
* the size of the types set, because some AccountData may not exist.
* If an empty set is provided, all the AccountData are retrieved
*/
fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent>
/**
* Observe the account data with the provided types. If an empty set is provided, all the AccountData are observed
*/
fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>>
/**
* Update the account data with the provided type and the provided account data content
*/
fun updateAccountData(type: String, content: Content, callback: MatrixCallback<Unit>? = null): Cancelable
}

View File

@@ -24,7 +24,7 @@ import im.vector.matrix.android.api.MatrixCallback
interface CacheService {
/**
* Clear the whole cached data, except credentials. Once done, the session is closed and has to be opened again
* Clear the whole cached data, except credentials. Once done, the sync has to be restarted by the sdk user.
*/
fun clearCache(callback: MatrixCallback<Unit>)
}

View File

@@ -29,8 +29,9 @@ data class ContentAttachmentData(
val width: Long? = 0,
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
val name: String? = null,
val queryUri: String,
val path: String,
val mimeType: String,
val mimeType: String?,
val type: Type
) : Parcelable {

View File

@@ -17,24 +17,35 @@
package im.vector.matrix.android.api.session.crypto
import android.content.Context
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
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.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
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.Event
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.NewSessionListener
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.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
interface CryptoService {
fun verificationService(): VerificationService
fun crossSigningService(): CrossSigningService
fun keysBackupService(): KeysBackupService
fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>)
fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>)
@@ -45,23 +56,19 @@ interface CryptoService {
fun isCryptoEnabled(): Boolean
fun getSasVerificationService(): SasVerificationService
fun getKeysBackupService(): KeysBackupService
fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean
fun setWarnOnUnknownDevices(warn: Boolean)
fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String)
fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String)
fun getUserDevices(userId: String): MutableList<MXDeviceInfo>
fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo>
fun setDevicesKnown(devices: List<MXDeviceInfo>, callback: MatrixCallback<Unit>?)
fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo?
fun deviceWithIdentityKey(senderKey: String, algorithm: String): CryptoDeviceInfo?
fun getMyDevice(): MXDeviceInfo
fun getMyDevice(): CryptoDeviceInfo
fun getGlobalBlacklistUnverifiedDevices(): Boolean
@@ -77,7 +84,7 @@ interface CryptoService {
fun setRoomBlacklistUnverifiedDevices(roomId: String)
fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo?
fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
fun reRequestRoomKeyForEvent(event: Event)
@@ -89,6 +96,8 @@ interface CryptoService {
fun getDevicesList(callback: MatrixCallback<DevicesListResponse>)
fun getDeviceInfo(deviceId: String, callback: MatrixCallback<DeviceInfo>)
fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int
fun isRoomEncrypted(roomId: String): Boolean
@@ -107,7 +116,15 @@ interface CryptoService {
fun shouldEncryptForInvitedMembers(roomId: String): Boolean
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>)
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>>)
fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo>
fun getLiveCryptoDeviceInfo(): LiveData<List<CryptoDeviceInfo>>
fun getLiveCryptoDeviceInfo(userId: String): LiveData<List<CryptoDeviceInfo>>
fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>>
fun addNewSessionListener(newSessionListener: NewSessionListener)

View File

@@ -18,7 +18,7 @@
package im.vector.matrix.android.api.session.crypto
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import org.matrix.olm.OlmException
@@ -36,7 +36,7 @@ sealed class MXCryptoError : Throwable() {
data class OlmError(val olmException: OlmException) : MXCryptoError()
data class UnknownDevice(val deviceList: MXUsersDevicesMap<MXDeviceInfo>) : MXCryptoError()
data class UnknownDevice(val deviceList: MXUsersDevicesMap<CryptoDeviceInfo>) : MXCryptoError()
enum class ErrorType {
ENCRYPTING_NOT_ENABLED,

View File

@@ -0,0 +1,71 @@
/*
* Copyright 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.crypto.crosssigning
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
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.UserTrustResult
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
interface CrossSigningService {
fun isCrossSigningVerified(): Boolean
fun isUserTrusted(otherUserId: String): Boolean
/**
* Will not force a download of the key, but will verify signatures trust chain.
* Checks that my trusted user key has signed the other user UserKey
*/
fun checkUserTrust(otherUserId: String): UserTrustResult
/**
* Initialize cross signing for this user.
* Users needs to enter credentials
*/
fun initializeCrossSigning(authParams: UserPasswordAuth?,
callback: MatrixCallback<Unit>? = null)
fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
uskKeyPrivateKey: String?,
sskPrivateKey: String?) : UserTrustResult
fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo?
fun getLiveCrossSigningKeys(userId: String): LiveData<Optional<MXCrossSigningInfo>>
fun getMyCrossSigningKeys(): MXCrossSigningInfo?
fun canCrossSign(): Boolean
fun trustUser(otherUserId: String,
callback: MatrixCallback<Unit>)
fun markMyMasterKeyAsTrusted()
/**
* Sign one of your devices and upload the signature
*/
fun trustDevice(deviceId: String,
callback: MatrixCallback<Unit>)
fun checkDeviceTrust(otherUserId: String,
otherDeviceId: String,
locallyTrusted: Boolean?): DeviceTrustResult
}

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.crypto.crosssigning
const val MASTER_KEY_SSSS_NAME = "m.cross_signing.master"
const val USER_SIGNING_KEY_SSSS_NAME = "m.cross_signing.user_signing"
const val SELF_SIGNING_KEY_SSSS_NAME = "m.cross_signing.self_signing"

View File

@@ -0,0 +1,38 @@
/*
* Copyright 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.crypto.crosssigning
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.KeyUsage
data class MXCrossSigningInfo(
val userId: String,
val crossSigningKeys: List<CryptoCrossSigningKey>
) {
fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true
&& selfSigningKey()?.trustLevel?.isVerified() == true
fun masterKey(): CryptoCrossSigningKey? = crossSigningKeys
.firstOrNull { it.usages?.contains(KeyUsage.MASTER.value) == true }
fun userKey(): CryptoCrossSigningKey? = crossSigningKeys
.firstOrNull { it.usages?.contains(KeyUsage.USER_SIGNING.value) == true }
fun selfSigningKey(): CryptoCrossSigningKey? = crossSigningKeys
.firstOrNull { it.usages?.contains(KeyUsage.SELF_SIGNING.value) == true }
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2019 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.crypto.sas
interface SasVerificationService {
fun addListener(listener: SasVerificationListener)
fun removeListener(listener: SasVerificationListener)
fun markedLocallyAsManuallyVerified(userId: String, deviceID: String)
fun getExistingTransaction(otherUser: String, tid: String): SasVerificationTransaction?
fun beginKeyVerificationSAS(userId: String, deviceID: String): String?
fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
// fun transactionUpdated(tx: SasVerificationTransaction)
interface SasVerificationListener {
fun transactionCreated(tx: SasVerificationTransaction)
fun transactionUpdated(tx: SasVerificationTransaction)
fun markedAsManuallyVerified(userId: String, deviceId: String)
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2019 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.crypto.sas
enum class SasVerificationTxState {
None,
// I have started a verification request
SendingStart,
Started,
// Other user/device sent me a request
OnStarted,
// I have accepted a request started by the other user/device
SendingAccept,
Accepted,
// My request has been accepted by the other user/device
OnAccepted,
// I have sent my public key
SendingKey,
KeySent,
// The other user/device has sent me his public key
OnKeyReceived,
// Short code is ready to be displayed
ShortCodeReady,
// I have compared the code and manually said that they match
ShortCodeAccepted,
SendingMac,
MacSent,
Verifying,
Verified,
// Global: The verification has been cancelled (by me or other), see cancelReason for details
Cancelled,
OnCancelled
}

View File

@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
package im.vector.matrix.android.api.session.crypto.verification
enum class CancelCode(val value: String, val humanReadable: String) {
User("m.user", "the user cancelled the verification"),
@@ -25,7 +26,9 @@ enum class CancelCode(val value: String, val humanReadable: String) {
UnexpectedMessage("m.unexpected_message", "the device received an unexpected message"),
InvalidMessage("m.invalid_message", "an invalid message was received"),
MismatchedKeys("m.key_mismatch", "Key mismatch"),
UserMismatchError("m.user_error", "User mismatch")
UserError("m.user_error", "User error"),
MismatchedUser("m.user_mismatch", "User mismatch"),
QrCodeInvalid("m.qr_code.invalid", "Invalid QR code")
}
fun safeValueOf(code: String?): CancelCode {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
package im.vector.matrix.android.api.session.crypto.verification
import androidx.annotation.StringRes

View File

@@ -14,9 +14,9 @@
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
package im.vector.matrix.android.api.session.crypto.verification
interface IncomingSasVerificationTransaction {
interface IncomingSasVerificationTransaction : SasVerificationTransaction {
val uxState: UxState
fun performAccept()

View File

@@ -14,9 +14,9 @@
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
package im.vector.matrix.android.api.session.crypto.verification
interface OutgoingSasVerificationRequest {
interface OutgoingSasVerificationTransaction : SasVerificationTransaction {
val uxState: UxState
enum class UxState {

View File

@@ -0,0 +1,40 @@
/*
* Copyright 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.crypto.verification
interface QrCodeVerificationTransaction : VerificationTransaction {
/**
* To use to display a qr code, for the other user to scan it
*/
val qrCodeText: String?
/**
* Call when you have scan the other user QR code
*/
fun userHasScannedOtherQrCode(otherQrCodeText: String)
/**
* Call when you confirm that other user has scanned your QR code
*/
fun otherUserScannedMyQrCode()
/**
* Call when you do not confirm that other user has scanned your QR code
*/
fun otherUserDidNotScannedMyQrCode()
}

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