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

Compare commits

...

502 Commits

Author SHA1 Message Date
Valere
dec591517c Merge branch 'release/0.18.0' 2020-03-11 13:41:49 +01:00
Valere
d5cbcdd4e7 prepare release 0.18.0 2020-03-11 13:41:17 +01:00
Valere
fe1af4e34f Merge pull request #1131 from vector-im/feature/fix_qr_self_verification
Fix / Self verification mode 1 was not working
2020-03-11 13:19:16 +01:00
Valere
49f2064439 Fix / Self verification mode 1 was not working 2020-03-11 11:43:23 +01:00
Valere
ff5b284b2e Try to remove rule that is breaking CI 2020-03-11 11:19:22 +01:00
Valere
d9085b1231 Merge pull request #1128 from vector-im/feature/remove_runblocking_crypto
Remove dangerous runBlocking (and un-needed)
2020-03-11 10:42:48 +01:00
Valere
a9074cdbbb Remove dangerous runBlocking (and un-nedded) 2020-03-10 17:25:04 +01:00
Benoit Marty
09946ecf1b Merge pull request #1117 from vector-im/feature/pusher_fix
Pusher fix
2020-03-10 11:34:11 +01:00
Benoit Marty
d21a536542 Merge pull request #1115 from vector-im/feature/work_manager_script
Add script to dumpsys work manager data
2020-03-06 17:31:19 +01:00
Benoit Marty
937d497a1f Revert the check box in case of error 2020-03-06 17:24:57 +01:00
Benoit Marty
4261b0e78a Fix pusher issue. It was set by mistake when the notification for this device was disabled 2020-03-06 17:19:48 +01:00
Benoit Marty
71446a1a74 Remove useless check 2020-03-06 17:11:23 +01:00
Benoit Marty
a29ec7b0be Pusher: add a menu to force a refresh of the local data 2020-03-06 16:48:48 +01:00
Benoit Marty
13036a5933 Pusher: update javadoc 2020-03-06 16:37:13 +01:00
Benoit Marty
35179509f2 Merge pull request #1111 from vector-im/feature/verification_code
Feature/verification code
2020-03-06 15:02:57 +01:00
Benoit Marty
5008524635 Add script to dumpsys work manager data 2020-03-06 14:52:09 +01:00
Benoit Marty
147766176c Merge pull request #1090 from vector-im/feature/notif
Restore the push rule settings
2020-03-06 14:43:33 +01:00
Benoit Marty
23862cb3d0 Merge branch 'develop' into feature/notif 2020-03-06 14:43:23 +01:00
Benoit Marty
2b8e2a312b Avoid use toImmutableList() 2020-03-06 14:39:33 +01:00
Benoit Marty
62fdb4c27a Use getOrPut() 2020-03-06 14:37:30 +01:00
Benoit Marty
b929a2f185 Merge pull request #1099 from vector-im/feature/fix_share_image
Share images from clear and encrypted rooms.
2020-03-06 14:27:47 +01:00
Benoit Marty
fb858bc112 Rename to respect naming convention #3 2020-03-06 14:04:02 +01:00
Benoit Marty
5d0e917f04 Rename to respect naming convention #2 2020-03-06 13:50:00 +01:00
Benoit Marty
e420070066 Rename to respect naming convention 2020-03-06 13:40:35 +01:00
Benoit Marty
4504308f25 Less "!!" 2020-03-06 13:34:12 +01:00
onurays
05683967c0 Code review fixes. 2020-03-06 12:07:38 +01:00
Benoit Marty
23c20acff1 Remove TODO 2020-03-06 10:11:30 +01:00
Benoit Marty
be5e6eaa93 Rename parameter type for code clarity 2020-03-06 10:10:16 +01:00
Onuray Sahin
555863fecc Merge pull request #1112 from vector-im/feature/navigate_to_profile_from_avatar
Open room member profile from avatar of the room member state event.
2020-03-06 09:42:32 +01:00
onurays
b187699a86 Open room member profile from avatar of the room member state event. 2020-03-05 18:44:34 +01:00
Benoit Marty
d891da39e6 Merge pull request #1104 from vector-im/feature/buildkite
Pipeline file for Buildkite is now hosted on another Github repository
2020-03-05 18:20:17 +01:00
Benoit Marty
f37cd8cddc Merge pull request #1110 from vector-im/feature/cleanup
Cleanup
2020-03-05 18:18:49 +01:00
Benoit Marty
2d456d93a7 Common code step 2 2020-03-05 18:16:05 +01:00
Benoit Marty
de36a28541 Common code step 1 2020-03-05 18:00:35 +01:00
Benoit Marty
4634b963a2 Code cleanup 2020-03-05 17:55:13 +01:00
Benoit Marty
b3f887ca28 Code quality 2020-03-05 17:30:34 +01:00
Benoit Marty
3425dd0a63 Make the tests compile and pass 2020-03-05 17:28:35 +01:00
Benoit Marty
2a774833ec More optimization 2020-03-05 17:11:52 +01:00
Benoit Marty
bda4bbb59c More optimization 2020-03-05 16:45:49 +01:00
Benoit Marty
0828f9270e Create asValidObject method - make it works 2020-03-05 16:23:56 +01:00
Benoit Marty
e326631752 Create asValidObject method - make it compiles #2 2020-03-05 15:39:01 +01:00
Benoit Marty
a3f8a53a52 Create asValidObject method - make it compiles. 2020-03-05 15:29:44 +01:00
Benoit Marty
7b5a50ec6e Create asValidObject method - not compiling 2020-03-05 12:06:19 +01:00
Benoit Marty
36c52d24a7 Move method to interface step 3 2020-03-05 11:24:35 +01:00
Benoit Marty
1b29c7bf91 Move method to interface step 2 2020-03-05 11:22:42 +01:00
Benoit Marty
b6aee04e24 Move method to interface step 1 2020-03-05 11:21:44 +01:00
Benoit Marty
a264dcf5c4 Lint: fix TextFields 2020-03-05 11:09:58 +01:00
Benoit Marty
37afc983c3 Fix typos. The typos has been fixed on Weblate by me. 2020-03-05 11:02:14 +01:00
Benoit Marty
b10e9c54b4 Add BinaryOperationInTimber lint rule and fix existing errors 2020-03-05 10:40:36 +01:00
onurays
03d2cd0639 Lint fix. 2020-03-04 17:57:57 +01:00
onurays
b7ad50a3ce Make mimeType private to encourage using getSafeMimeType() method. 2020-03-04 16:52:52 +01:00
onurays
9cbaadedfb Unused context parameter is removed. 2020-03-04 16:52:08 +01:00
Benoit Marty
d6cdcc60d1 Remove unused png files 2020-03-04 16:48:13 +01:00
Onuray Sahin
1680cd8d8e Merge branch 'develop' into feature/fix_share_image 2020-03-04 16:27:01 +01:00
onurays
d4384328fe Use "image/jpeg" instead of "image/jpg" 2020-03-04 16:26:09 +01:00
Benoit Marty
5fd8425289 Remove unused files 2020-03-04 16:09:37 +01:00
Benoit Marty
dcb618c257 Merge pull request #1103 from vector-im/feature/space_in_passwords
Detect spaces in password if user fails to login (#1038)
2020-03-04 14:12:20 +01:00
Benoit Marty
277ad9e042 Merge branch 'develop' into feature/space_in_passwords 2020-03-04 14:12:12 +01:00
onurays
26d387cc12 Support sharing other media types. 2020-03-04 13:47:48 +01:00
Benoit Marty
4aebe6d303 Disable TravisCI checks, now done by Buildkite 2020-03-04 12:23:25 +01:00
Benoit Marty
7eccda6e25 Pipeline file for Buildkite is now hosted on another Github repository
https://github.com/matrix-org/pipelines/blob/master/riotx-android/pipeline.yml
2020-03-04 12:20:09 +01:00
Benoit Marty
78bc2bbaa5 AndroidStudio cleanup 2020-03-04 12:15:33 +01:00
Benoit Marty
cde068f267 Merge pull request #1079 from vector-im/feature/ftue
FTUE: do not display a different color when encrypting message when not in developer mode.
2020-03-04 12:14:24 +01:00
Benoit Marty
c8f43e73e2 Merge branch 'develop' into feature/ftue 2020-03-04 12:14:07 +01:00
Benoit Marty
f106f92d7e Merge pull request #1085 from vector-im/feature/ktlint_upgrade
ktlint 0.36.0, Chromebook support, Camera for QR code scanning
2020-03-04 12:11:19 +01:00
Benoit Marty
fa0e07e146 Merge branch 'develop' into feature/ktlint_upgrade 2020-03-04 12:10:56 +01:00
Benoit Marty
d6df0e451c Detect spaces in password if user fail to login (#1038) 2020-03-04 10:39:10 +01:00
Benoit Marty
0121eee5b8 changelog 2020-03-04 09:37:32 +01:00
Benoit Marty
30e46445ca Add documentation to install environment and run integration tests (#1098)
Add documentation to install environment and run integration tests
2020-03-04 09:35:09 +01:00
onurays
7158554ee2 Add fallback logic only if the mode is thumbnail. 2020-03-03 16:56:22 +01:00
onurays
34c5537436 Fix fallback to full image. 2020-03-03 16:42:33 +01:00
onurays
d09ac8fbce Try to show full image as the fallback of the thumbnail. 2020-03-03 14:54:05 +01:00
onurays
319667096f Return Try.Failure instead of throwing exception. 2020-03-03 13:41:40 +01:00
onurays
c4f2eeeab7 Changelog added. 2020-03-03 10:40:58 +01:00
onurays
5f14516dec Share images from clear and encrypted rooms. 2020-03-03 10:39:24 +01:00
Benoit Marty
5ff670b184 Merge pull request #1093 from vector-im/feature/plain_text
Add support for `/plain` command (#12)
2020-03-02 19:37:05 +01:00
Benoit Marty
b7ff546f66 Add support for /plain command (#12) 2020-03-01 09:59:00 +01:00
Benoit Marty
c13b636bae Code cleanup 2020-02-29 10:05:48 +01:00
Benoit Marty
a05caf6f0e Update the 2 notification settings screens by moving some pref to the root notification screen 2020-02-29 09:49:21 +01:00
Benoit Marty
3462df405c Fix GPlay build issue 2020-02-29 09:42:35 +01:00
Benoit Marty
6009026e2f Notification settings: create a category for troubleshhot 2020-02-29 09:35:26 +01:00
Benoit Marty
06e0af9a5b small mistake 2020-02-28 22:12:30 +01:00
Benoit Marty
3987a68a9a Changelog 2020-02-28 22:10:07 +01:00
Benoit Marty
1228fcda0d Rename class 2020-02-28 19:25:29 +01:00
Benoit Marty
c20de4feb0 Cleanup 2020-02-28 19:13:22 +01:00
Benoit Marty
957b9eee23 Cleanup 2020-02-28 19:06:26 +01:00
Benoit Marty
e5eb36e917 Cleanup 2020-02-28 19:04:30 +01:00
Benoit Marty
551604cdcb Add missing push rules 2020-02-28 19:02:13 +01:00
Benoit Marty
8a2bafec5f Restore push rule settings - fix issues 2020-02-28 18:35:51 +01:00
Benoit Marty
3013e311a4 Restore push rule settings - WIP 2020-02-28 16:51:11 +01:00
Benoit Marty
1c35d07acc PushRuleService.getPushRules() now returns a RuleSet. Use getAllRules() on this object to get all the rules. 2020-02-28 16:29:58 +01:00
Benoit Marty
039924436f Json parsing 2020-02-28 14:51:35 +01:00
Benoit Marty
41b4f412c4 Merge pull request #1089 from vector-im/feature/crash_in_attachment_preview
Fix crash on attachment preview screen (#1088)
2020-02-28 14:49:36 +01:00
Benoit Marty
2abe29300b Merge pull request #1087 from vector-im/feature/verification_cleanup
Verification cleanup
2020-02-28 14:48:27 +01:00
Valere
779026b0af Fix / mark master key as trusted after self verif 2020-02-28 11:46:32 +01:00
Benoit Marty
151fec5ce0 Fix crash on attachment preview screen (#1088) 2020-02-28 11:38:30 +01:00
Benoit Marty
b1b8513da4 Create fromBase64Safe() to parse data received from external source 2020-02-27 19:17:14 +01:00
Benoit Marty
0a9008a73d Be robust if other client sends padded base64 in the reciprocate 2020-02-27 18:35:05 +01:00
Benoit Marty
1ead2778c2 ... and rename the method fromBase64NoPadding() to fromBase64() 2020-02-27 18:33:57 +01:00
Benoit Marty
8299487f6d Avoid using encoder flag to decode Base64 string... 2020-02-27 18:13:42 +01:00
Benoit Marty
ceab0903cf Improve code - TU passed 2020-02-27 18:09:37 +01:00
Benoit Marty
77dd911054 Code quality 2020-02-27 17:42:25 +01:00
Benoit Marty
a914381090 Verification with QrCode: ensure there is a Camera to scan QrCodes. 2020-02-27 17:03:48 +01:00
Benoit Marty
9f28738fd6 Restore availability to Chromebooks (#932) 2020-02-27 15:41:35 +01:00
Benoit Marty
36a848471f Enable other lint check (no change necessary in source) 2020-02-27 15:27:46 +01:00
Benoit Marty
12861aacda lint: check negative margin 2020-02-27 15:11:43 +01:00
Benoit Marty
d85776297d Lint: fix IconDuplicates by remove unused resources 2020-02-27 14:56:18 +01:00
Benoit Marty
50031bef50 Lint: fix IconDipSize and IconDuplicatesConfig error (by removing unused resource) 2020-02-27 14:45:26 +01:00
Benoit Marty
c0d6b9d130 Lint fix IconXmlAndPng error 2020-02-27 14:35:57 +01:00
Benoit Marty
a1466e299b Fix missing recycle call and ensure this mistake will not happen again 2020-02-27 14:30:49 +01:00
Benoit Marty
bc9493d5b9 Upgrade ktlint to version 0.36.0 2020-02-27 12:48:17 +01:00
Benoit Marty
9bb4c7ed25 Version++ 2020-02-27 12:34:33 +01:00
Benoit Marty
128f3493b7 Merge branch 'release/0.17.0' 2020-02-27 12:32:36 +01:00
Benoit Marty
65e5a04836 Merge branch 'release/0.17.0' into develop 2020-02-27 12:32:35 +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
a69049645a FTUE: do not display a different color when encrypting message when not in developer mode. 2020-02-26 11:25:37 +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
362799ac08 Merge branch 'release/0.14.3' into develop 2020-02-03 16:15:52 +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
Ganard
37230b0614 Fix issues with read marker and jumpToBottom 2020-01-31 18:09:34 +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
Ganard
5e1b59f9d3 Timeline: handle an in memory local echo to make the UI snappier 2020-01-30 17:13:44 +01:00
ganfra
9fc3fa7f19 Update some libs and remove incremental from dagger 2020-01-29 21:14:38 +01:00
ganfra
71a02a58af Sync/Timeline: handle displayName isUnique 2020-01-29 17:30:31 +01:00
ganfra
7f72af426b Timeline: fix getContext 2020-01-29 16:02:53 +01:00
Ganard
a8f783bbfa Add state events to chunks 2020-01-28 18:59:21 +01:00
Ganard
bf7c53ecab Sync/pagination: get a working version 2020-01-28 14:46:26 +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
Ganard
f3db43f317 Ellipsize: introduce EllipsizingTextView 2020-01-27 12:41:43 +01:00
ganfra
3a89a30056 Continue reworking sync/timeline events handling 2020-01-25 18:59:45 +01:00
Ganard
1d8b81bb04 Try reworking events/timeline process [WIP] 2020-01-24 18:43:35 +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
ganfra
c65f25d7ae Rx: fix startWith on mainThread 2020-01-23 10:18:22 +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
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
ganfra
2bddf61afe Update realm to 6.1.0: should fix some of the native crashes 2020-01-21 15:15:29 +01:00
ganfra
d1b8d81fb1 Fix double read receipts 2020-01-21 14:17:04 +01:00
917 changed files with 20324 additions and 8797 deletions

View File

@@ -1,88 +0,0 @@
# Use Docker file from https://hub.docker.com/r/runmymind/docker-android-sdk
# Last docker plugin version can be found here:
# https://github.com/buildkite-plugins/docker-buildkite-plugin/releases
# We propagate the environment to the container (sse https://github.com/buildkite-plugins/docker-buildkite-plugin#propagate-environment-optional-boolean)
steps:
- label: "Compile and run Unit tests"
agents:
# We use a medium sized instance instead of the normal small ones because
# gradle build can be memory hungry
queue: "medium"
commands:
- "./gradlew clean test --stacktrace"
plugins:
- docker#v3.1.0:
image: "runmymind/docker-android-sdk"
propagate-environment: true
- label: "Compile Android tests"
agents:
# We use a medium sized instance instead of the normal small ones because
# gradle build can be memory hungry
queue: "medium"
commands:
- "./gradlew clean assembleAndroidTest --stacktrace"
plugins:
- docker#v3.1.0:
image: "runmymind/docker-android-sdk"
propagate-environment: true
- label: "Assemble GPlay Debug version"
agents:
# We use a xlarge sized instance instead of the normal small ones because
# gradle build can be memory hungry
queue: "xlarge"
commands:
- "./gradlew clean lintGplayRelease assembleGplayDebug --stacktrace"
artifact_paths:
- "vector/build/outputs/apk/gplay/debug/*.apk"
branches: "!master"
plugins:
- docker#v3.1.0:
image: "runmymind/docker-android-sdk"
propagate-environment: true
- label: "Assemble FDroid Debug version"
agents:
# We use a xlarge sized instance instead of the normal small ones because
# gradle build can be memory hungry
queue: "xlarge"
commands:
- "./gradlew clean lintFdroidRelease assembleFdroidDebug --stacktrace"
artifact_paths:
- "vector/build/outputs/apk/fdroid/debug/*.apk"
branches: "!master"
plugins:
- docker#v3.1.0:
image: "runmymind/docker-android-sdk"
propagate-environment: true
- label: "Build Google Play unsigned APK"
agents:
# We use a xlarge sized instance instead of the normal small ones because
# gradle build can be memory hungry
queue: "xlarge"
commands:
- "./gradlew clean assembleGplayRelease --stacktrace"
artifact_paths:
- "vector/build/outputs/apk/gplay/release/*.apk"
branches: "master"
plugins:
- docker#v3.1.0:
image: "runmymind/docker-android-sdk"
propagate-environment: true
# Code quality
- label: "Code quality"
command:
- "./tools/check/check_code_quality.sh"
- label: "ktlint"
command:
- "curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint"
- "./ktlint --android --experimental -v"
plugins:
- docker#v3.1.0:
image: "openjdk"

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

5
.gitignore vendored
View File

@@ -4,6 +4,7 @@
# idea files: exclude everything except dictionnaries
.idea/caches
.idea/libraries
.idea/inspectionProfiles
.idea/*.xml
.DS_Store
/build
@@ -13,7 +14,3 @@
/tmp
ktlint
.idea/copyright/New_vector.xml
.idea/copyright/profiles_settings.xml
.idea/copyright/New_Vector_Ltd.xml

View File

@@ -1,8 +1,6 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<option name="RIGHT_MARGIN" value="160" />
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>

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

@@ -19,9 +19,11 @@
<w>msisdn</w>
<w>pbkdf</w>
<w>pkcs</w>
<w>riotx</w>
<w>signin</w>
<w>signout</w>
<w>signup</w>
<w>ssss</w>
<w>threepid</w>
</words>
</dictionary>

View File

@@ -49,12 +49,12 @@ script:
# Build Android test (assembleAndroidTest) (disabled for now)
# Code quality (lintGplayRelease lintFdroidRelease)
# Split into two steps because if a task contain Fdroid, PlayService will be disabled
- ./gradlew clean assembleGplayRelease lintGplayRelease --stacktrace
- ./gradlew clean assembleFdroidRelease lintFdroidRelease --stacktrace
# Done by Buildkite now: - ./gradlew clean assembleGplayRelease lintGplayRelease --stacktrace
# Done by Buildkite now: - ./gradlew clean assembleFdroidRelease lintFdroidRelease --stacktrace
# Run unitary test (Disable for now, see https://travis-ci.org/vector-im/riot-android/builds/502504370)
# - ./gradlew testGplayReleaseUnitTest --stacktrace
# Other code quality check
- ./tools/check/check_code_quality.sh
# Done by Buildkite now: - ./tools/check/check_code_quality.sh
- ./tools/travis/check_pr.sh
# Check that indonesians file are identical. Due to Android issue, the resource folder must be value-in/, and Weblate export data into value-id/.
- diff ./vector/src/main/res/values-id/strings.xml ./vector/src/main/res/values-in/strings.xml
# Done by Buildkite now: - diff ./vector/src/main/res/values-id/strings.xml ./vector/src/main/res/values-in/strings.xml

View File

@@ -1,3 +1,97 @@
Changes in RiotX 0.18.0 (2020-03-11)
===================================================
Improvements 🙌:
- Share image and other media from e2e rooms (#677)
- Add support for `/plain` command (#12)
- Detect spaces in password if user fail to login (#1038)
- FTUE: do not display a different color when encrypting message when not in developer mode.
- Open room member profile from avatar of the room member state event (#935)
- Restore the push rules configuration in the settings
Bugfix 🐛:
- Fix crash on attachment preview screen (#1088)
- "Share" option is not appearing in encrypted rooms for images (#1031)
- Set "image/jpeg" as MIME type of images instead of "image/jpg" (#1075)
- Self verification via QR code is failing (#1130)
SDK API changes ⚠️:
- PushRuleService.getPushRules() now returns a RuleSet. Use getAllRules() on this object to get all the rules.
Build 🧱:
- Upgrade ktlint to version 0.36.0
- Pipeline file for Buildkite is now hosted on another Github repository: https://github.com/matrix-org/pipelines/blob/master/riotx-android/pipeline.yml
Other changes:
- Restore availability to Chromebooks (#932)
- Add a [documentation](./docs/integration_tests.md) to run integration tests
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)
===================================================
@@ -349,15 +443,17 @@ Features ✨:
Improvements 🙌:
-
Other changes:
-
Bugfix 🐛:
-
Translations 🗣:
-
SDK API changes ⚠️:
-
Build 🧱:
-
Other changes:
-

View File

@@ -82,6 +82,8 @@ Make sure the following commands execute without any error:
RiotX is currently supported on Android KitKat (API 19+): please test your change on an Android device (or Android emulator) running with API 19. Many issues can happen (including crashes) on older devices.
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
You should consider adding Unit tests with your PR, and also integration tests (AndroidTest). Please refer to [this document](./docs/integration_tests.md) to install and run the integration test environment.
### Internationalisation
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).

View File

@@ -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 {
@@ -47,23 +51,11 @@ allprojects {
jcenter()
}
tasks.withType(JavaCompile).all {
options.compilerArgs += [
'-Adagger.gradle.incremental=enabled'
]
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
// Warnings are potential errors, so stop ignoring them
kotlinOptions.allWarningsAsErrors = true
}
afterEvaluate {
extensions.findByName("kapt")?.arguments {
arg("dagger.gradle.incremental", "enabled")
}
}
}
task clean(type: Delete) {

97
docs/integration_tests.md Normal file
View File

@@ -0,0 +1,97 @@
# Integration tests
Integration tests are useful to ensure that the code works well for any use cases.
They can also be used as sample on how to use the Matrix SDK.
In a ideal world, every API of the SDK should be covered by integration tests. For the moment, we have test mainly for the Crypto part, which is the tricky part. But it covers quite a lot of features: accounts creation, login to existing account, send encrypted messages, keys backup, verification, etc.
The Matrix SDK is able to open multiple sessions, for the same user, of for different users. This way we can test communication between several sessions on a single device.
## Pre requirements
Integration tests need a homeserver running on localhost.
The documentation describes what we do to have one, using [Synapse](https://github.com/matrix-org/synapse/), which is the Matrix reference homeserver.
## Install and run Synapse
Steps:
- Install virtualenv
```bash
python3 -m pip install virtualenv
```
- Clone Synapse repository
```bash
git clone -b develop https://github.com/matrix-org/synapse.git
```
or
```bash
git clone -b develop git@github.com:matrix-org/synapse.git
```
You should have the develop branch cloned by default.
- Run synapse, from the Synapse folder you just cloned
```bash
virtualenv -p python3 env
source env/bin/activate
pip install -e .
demo/start.sh --no-rate-limit
```
Alternatively, to install the latest Synapse release package (and not a cloned branch) you can run the following instead of `pip install -e .`:
```bash
pip install matrix-synapse
```
You should now have 3 running federated Synapse instances 🎉, at http://127.0.0.1:8080/, http://127.0.0.1:8081/ and http://127.0.0.1:8082/, which should display a "It Works! Synapse is running" message.
## Run the test
It's recommended to run tests using an Android Emulator and not a real device. First reason for that is that the tests will use http://10.0.2.2:8080 to connect to Synapse, which run locally on your machine.
You can run all the tests in the `androidTest` folders.
## Stop Synapse
To stop Synapse, you can run the following commands:
```bash
./demo/stop.sh
```
And you can deactivate the virtualenv:
```bash
deactivate
```
## Troubleshoot
You'll need python3 to be able to run synapse
### Android Emulator does cannot reach the homeserver
Try on the Emulator browser to open "http://10.0.2.2:8080". You should see the "Synapse is running" message.
### virtualenv command fails
You can try using
```bash
python3 -m venv env
```
or
```bash
python3 -m virtualenv env
```
instead of
```bash
virtualenv -p python3 env
```

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

@@ -16,8 +16,6 @@
package im.vector.matrix.rx
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
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.room.Room
import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams
@@ -30,114 +28,43 @@ 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 im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.functions.BiFunction
import timber.log.Timber
class RxRoom(private val room: Room, private val session: Session) {
class RxRoom(private val room: Room) {
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
val summaryObservable = room.getRoomSummaryLive()
return room.getRoomSummaryLive()
.asObservable()
.startWith(room.roomSummary().toOptional())
.doOnNext { Timber.v("RX: summary emitted for: ${it.getOrNull()?.roomId}") }
val memberIdsChangeObservable = summaryObservable
.map {
it.getOrNull()?.let { roomSummary ->
if (roomSummary.isEncrypted) {
// Return the list of other users
roomSummary.otherMemberIds + listOf(session.myUserId)
} else {
// Return an empty list, the room is not encrypted
emptyList()
}
}.orEmpty()
}.distinctUntilChanged()
.doOnNext { Timber.v("RX: memberIds emitted. Size: ${it.size}") }
// Observe the device info of the users in the room
val cryptoDeviceInfoObservable = memberIdsChangeObservable
.switchMap { membersIds ->
session.getLiveCryptoDeviceInfo(membersIds)
.asObservable()
.map {
// If any key change, emit the userIds list
membersIds
}
.startWith(membersIds)
.doOnNext { Timber.v("RX: CryptoDeviceInfo emitted. Size: ${it.size}") }
}
.doOnNext { Timber.v("RX: cryptoDeviceInfo emitted 2. Size: ${it.size}") }
val roomEncryptionTrustLevelObservable = cryptoDeviceInfoObservable
.map { userIds ->
if (userIds.isEmpty()) {
Optional<RoomEncryptionTrustLevel>(null)
} else {
session.getCrossSigningService().getTrustLevelForUsers(userIds).toOptional()
}
}
.doOnNext { Timber.v("RX: roomEncryptionTrustLevel emitted: ${it.getOrNull()?.name}") }
return Observable
.combineLatest<Optional<RoomSummary>, Optional<RoomEncryptionTrustLevel>, Optional<RoomSummary>>(
summaryObservable,
roomEncryptionTrustLevelObservable,
BiFunction { summary, level ->
summary.getOrNull()?.copy(
roomEncryptionTrustLevel = level.getOrNull()
).toOptional()
}
)
.doOnNext { Timber.v("RX: final room summary emitted for ${it.getOrNull()?.roomId}") }
.startWithCallable { room.roomSummary().toOptional() }
}
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable<List<RoomMemberSummary>> {
val roomMembersObservable = room.getRoomMembersLive(queryParams).asObservable()
.startWith(room.getRoomMembers(queryParams))
.doOnNext { Timber.v("RX: room members emitted. Size: ${it.size}") }
// TODO Do it only for room members of the room (switchMap)
val cryptoDeviceInfoObservable = session.getLiveCryptoDeviceInfo().asObservable()
.startWith(emptyList<CryptoDeviceInfo>())
.doOnNext { Timber.v("RX: cryptoDeviceInfo emitted. Size: ${it.size}") }
return Observable
.combineLatest<List<RoomMemberSummary>, List<CryptoDeviceInfo>, List<RoomMemberSummary>>(
roomMembersObservable,
cryptoDeviceInfoObservable,
BiFunction { summaries, _ ->
summaries.map {
if (room.isEncrypted()) {
it.copy(
// Get the trust level of a virtual room with only this user
userEncryptionTrustLevel = session.getCrossSigningService().getTrustLevelForUsers(listOf(it.userId))
)
} else {
it
}
}
}
)
.doOnNext { Timber.v("RX: final room members emitted. Size: ${it.size}") }
return room.getRoomMembersLive(queryParams).asObservable()
.startWithCallable {
room.getRoomMembers(queryParams)
}
}
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
return room.getEventAnnotationsSummaryLive(eventId).asObservable()
.startWith(room.getEventAnnotationsSummary(eventId).toOptional())
.startWithCallable {
room.getEventAnnotationsSummary(eventId).toOptional()
}
}
fun liveTimelineEvent(eventId: String): Observable<Optional<TimelineEvent>> {
return room.getTimeLineEventLive(eventId).asObservable()
.startWith(room.getTimeLineEvent(eventId).toOptional())
.startWithCallable {
room.getTimeLineEvent(eventId).toOptional()
}
}
fun liveStateEvent(eventType: String): Observable<Optional<Event>> {
return room.getStateEventLive(eventType).asObservable()
.startWith(room.getStateEvent(eventType).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>> {
@@ -170,6 +97,6 @@ class RxRoom(private val room: Room, private val session: Session) {
}
}
fun Room.rx(session: Session): RxRoom {
return RxRoom(this, session)
fun Room.rx(): RxRoom {
return RxRoom(this)
}

View File

@@ -31,50 +31,31 @@ 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
import io.reactivex.functions.BiFunction
import timber.log.Timber
class RxSession(private val session: Session) {
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Observable<List<RoomSummary>> {
val summariesObservable = session.getRoomSummariesLive(queryParams).asObservable()
.startWith(session.getRoomSummaries(queryParams))
.doOnNext { Timber.v("RX: summaries emitted: size: ${it.size}") }
val cryptoDeviceInfoObservable = session.getLiveCryptoDeviceInfo().asObservable()
.startWith(emptyList<CryptoDeviceInfo>())
.doOnNext { Timber.v("RX: crypto device info emitted: size: ${it.size}") }
return Observable
.combineLatest<List<RoomSummary>, List<CryptoDeviceInfo>, List<RoomSummary>>(
summariesObservable,
cryptoDeviceInfoObservable,
BiFunction { summaries, _ ->
summaries.map {
if (it.isEncrypted) {
it.copy(
roomEncryptionTrustLevel = session.getCrossSigningService()
.getTrustLevelForUsers(it.otherMemberIds + session.myUserId)
)
} else {
it
}
}
}
)
.doOnNext { Timber.d("RX: final summaries emitted: size: ${it.size}") }
return session.getRoomSummariesLive(queryParams).asObservable()
.startWithCallable {
session.getRoomSummaries(queryParams)
}
}
fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Observable<List<GroupSummary>> {
return session.getGroupSummariesLive(queryParams).asObservable()
.startWith(session.getGroupSummaries(queryParams))
.startWithCallable {
session.getGroupSummaries(queryParams)
}
}
fun liveBreadcrumbs(): Observable<List<RoomSummary>> {
return session.getBreadcrumbsLive().asObservable()
.startWith(session.getBreadcrumbs())
.startWithCallable {
session.getBreadcrumbs()
}
}
fun liveSyncState(): Observable<SyncState> {
@@ -87,7 +68,9 @@ class RxSession(private val session: Session) {
fun liveUser(userId: String): Observable<Optional<User>> {
return session.getUserLive(userId).asObservable()
.startWith(session.getUser(userId).toOptional())
.startWithCallable {
session.getUser(userId).toOptional()
}
}
fun liveUsers(): Observable<List<User>> {
@@ -112,10 +95,10 @@ class RxSession(private val session: Session) {
session.searchUsersDirectory(search, limit, excludedUserIds, it)
}
fun joinRoom(roomId: String,
fun joinRoom(roomIdOrAlias: String,
reason: String? = null,
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
session.joinRoom(roomId, reason, viaServers, it)
session.joinRoom(roomIdOrAlias, reason, viaServers, it)
}
fun getRoomIdByAlias(roomAlias: String,
@@ -128,12 +111,23 @@ class RxSession(private val session: Session) {
}
fun liveUserCryptoDevices(userId: String): Observable<List<CryptoDeviceInfo>> {
return session.getLiveCryptoDeviceInfo(userId).asObservable()
return session.cryptoService().getLiveCryptoDeviceInfo(userId).asObservable().startWithCallable {
session.cryptoService().getCryptoDeviceInfo(userId)
}
}
fun liveCrossSigningInfo(userId: String): Observable<Optional<MXCrossSigningInfo>> {
return session.getCrossSigningService().getLiveCrossSigningKeys(userId).asObservable()
.startWith(session.getCrossSigningService().getUserCrossSigningKeys(userId).toOptional())
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

@@ -92,10 +92,11 @@ 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"
@@ -104,14 +105,13 @@ dependencies {
implementation "androidx.appcompat:appcompat:1.1.0"
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"
@@ -119,13 +119,14 @@ dependencies {
// Image
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-beta02"
implementation "androidx.work:work-runtime-ktx:2.3.3"
// FP
implementation "io.arrow-kt:arrow-core:$arrow_version"
@@ -167,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

@@ -38,9 +38,7 @@ class AccountCreationTest : InstrumentedTest {
fun createAccountTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
commonTestHelper.signout(session)
session.close()
commonTestHelper.signOutAndClose(session)
}
@Test
@@ -50,14 +48,14 @@ class AccountCreationTest : InstrumentedTest {
// Log again to the same account
val session2 = commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
session.close()
session2.close()
commonTestHelper.signOutAndClose(session)
commonTestHelper.signOutAndClose(session2)
}
@Test
fun simpleE2eTest() {
val res = cryptoTestHelper.doE2ETestWithAliceInARoom()
res.close()
res.cleanUp(commonTestHelper)
}
}

View File

@@ -28,7 +28,10 @@ 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
@@ -113,25 +116,35 @@ class CommonTestHelper(context: Context) {
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(nbOfMessages)
val onEventSentListener = object : Timeline.Listener {
val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
override fun onNewTimelineEvents(eventIds: List<String>) {
// noop
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
// TODO Count only new messages?
if (snapshot.count { it.root.type == EventType.MESSAGE } == nbOfMessages) {
sentEvents.addAll(snapshot.filter { it.root.type == EventType.MESSAGE })
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.addListener(onEventSentListener)
timeline.start()
timeline.addListener(timelineListener)
for (i in 0 until nbOfMessages) {
room.sendTextMessage(message + " #" + (i + 1))
}
await(latch)
timeline.removeListener(onEventSentListener)
timeline.removeListener(timelineListener)
timeline.dispose()
// Check that all events has been created
assertEquals(nbOfMessages.toLong(), sentEvents.size.toLong())
@@ -279,11 +292,10 @@ class CommonTestHelper(context: Context) {
/**
* Clear all provided sessions
*/
fun Iterable<Session>.close() = forEach { it.close() }
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
fun signout(session: Session) {
val lock = CountDownLatch(1)
session.signOut(true, TestMatrixCallback(lock))
await(lock)
fun signOutAndClose(session: Session) {
doSync<Unit> { session.signOut(true, it) }
session.close()
}
}

View File

@@ -23,9 +23,9 @@ data class CryptoTestData(val firstSession: Session,
val secondSession: Session? = null,
val thirdSession: Session? = null) {
fun close() {
firstSession.close()
secondSession?.close()
secondSession?.close()
fun cleanUp(testHelper: CommonTestHelper) {
testHelper.signOutAndClose(firstSession)
secondSession?.let { testHelper.signOutAndClose(it) }
thirdSession?.let { testHelper.signOutAndClose(it) }
}
}

View File

@@ -41,16 +41,15 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import java.util.Arrays
import java.util.HashMap
import java.util.concurrent.CountDownLatch
class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
val messagesFromAlice: List<String> = Arrays.asList("0 - Hello I'm Alice!", "4 - Go!")
val messagesFromBob: List<String> = Arrays.asList("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
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.")
val defaultSessionParams = SessionTestParams(true)
private val defaultSessionParams = SessionTestParams(true)
/**
* @return alice session
@@ -58,34 +57,23 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
fun doE2ETestWithAliceInARoom(): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
var roomId: String? = null
val lock1 = CountDownLatch(1)
val roomId = mTestHelper.doSync<String> {
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it)
}
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), object : TestMatrixCallback<String>(lock1) {
override fun onSuccess(data: String) {
roomId = data
super.onSuccess(data)
}
})
val room = aliceSession.getRoom(roomId)!!
mTestHelper.await(lock1)
assertNotNull(roomId)
mTestHelper.doSync<Unit> {
room.enableEncryption(callback = it)
}
val room = aliceSession.getRoom(roomId!!)!!
val lock2 = CountDownLatch(1)
room.enableEncryptionWithAlgorithm(MXCRYPTO_ALGORITHM_MEGOLM, object : TestMatrixCallback<Unit>(lock2) {})
mTestHelper.await(lock2)
return CryptoTestData(aliceSession, roomId!!)
return CryptoTestData(aliceSession, roomId)
}
/**
* @return alice and bob sessions
*/
fun doE2ETestWithAliceAndBobInARoom(): CryptoTestData {
val statuses = HashMap<String, String>()
val cryptoTestData = doE2ETestWithAliceInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
@@ -94,7 +82,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
val lock1 = CountDownLatch(2)
val lock1 = CountDownLatch(1)
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
@@ -103,7 +91,6 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) {
if (t?.isNotEmpty() == true) {
statuses["onNewRoom"] = "onNewRoom"
lock1.countDown()
bobRoomSummariesLive.removeObserver(this)
}
@@ -114,26 +101,20 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
bobRoomSummariesLive.observeForever(newRoomObserver)
}
aliceRoom.invite(bobSession.myUserId, callback = object : TestMatrixCallback<Unit>(lock1) {
override fun onSuccess(data: Unit) {
statuses["invite"] = "invite"
super.onSuccess(data)
}
})
mTestHelper.doSync<Unit> {
aliceRoom.invite(bobSession.myUserId, callback = it)
}
mTestHelper.await(lock1)
assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))
val lock2 = CountDownLatch(2)
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) {
statuses["AliceJoin"] = "AliceJoin"
lock2.countDown()
lock.countDown()
bobRoomSummariesLive.removeObserver(this)
}
}
@@ -143,19 +124,15 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
bobRoomSummariesLive.observeForever(roomJoinedObserver)
}
bobSession.joinRoom(aliceRoomId, callback = TestMatrixCallback(lock2))
mTestHelper.doSync<Unit> { bobSession.joinRoom(aliceRoomId, callback = it) }
mTestHelper.await(lock2)
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))
assertTrue(statuses.toString() + "", statuses.containsKey("AliceJoin"))
// bobSession.dataHandler.removeListener(bobEventListener)
return CryptoTestData(aliceSession, aliceRoomId, bobSession)
}
@@ -230,77 +207,54 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
val aliceRoomId = cryptoTestData.roomId
val bobSession = cryptoTestData.secondSession!!
bobSession.setWarnOnUnknownDevices(false)
bobSession.cryptoService().setWarnOnUnknownDevices(false)
aliceSession.setWarnOnUnknownDevices(false)
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
var lock = CountDownLatch(1)
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 size = snapshot.filter { it.root.senderId != bobSession.myUserId && it.root.getClearType() == EventType.MESSAGE }
.size
val messages = snapshot.filter { it.root.getClearType() == EventType.MESSAGE }
.groupBy { it.root.senderId!! }
if (size == 3) {
// 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(10))
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(20))
bobTimeline.start()
bobTimeline.addListener(bobEventsListener)
val results = HashMap<String, Any>()
// bobSession.dataHandler.addListener(object : MXEventListener() {
// override fun onToDeviceEvent(event: Event) {
// results["onToDeviceEvent"] = event
// lock.countDown()
// }
// })
// Alice sends a message
roomFromAlicePOV.sendTextMessage(messagesFromAlice[0])
assertTrue(results.containsKey("onToDeviceEvent"))
// assertEquals(1, messagesReceivedByBobCount)
// Bob send a message
lock = CountDownLatch(1)
// Bob send 3 messages
roomFromBobPOV.sendTextMessage(messagesFromBob[0])
// android does not echo the messages sent from itself
// messagesReceivedByBobCount++
mTestHelper.await(lock)
// assertEquals(2, messagesReceivedByBobCount)
// Bob send a message
lock = CountDownLatch(1)
roomFromBobPOV.sendTextMessage(messagesFromBob[1])
// android does not echo the messages sent from itself
// messagesReceivedByBobCount++
mTestHelper.await(lock)
// assertEquals(3, messagesReceivedByBobCount)
// Bob send a message
lock = CountDownLatch(1)
roomFromBobPOV.sendTextMessage(messagesFromBob[2])
// android does not echo the messages sent from itself
// messagesReceivedByBobCount++
mTestHelper.await(lock)
// assertEquals(4, messagesReceivedByBobCount)
// Alice sends a message
lock = CountDownLatch(2)
roomFromAlicePOV.sendTextMessage(messagesFromAlice[1])
mTestHelper.await(lock)
// assertEquals(5, messagesReceivedByBobCount)
bobTimeline.removeListener(bobEventsListener)
bobTimeline.dispose()
return cryptoTestData
}
@@ -335,18 +289,14 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
fun createFakeMegolmBackupAuthData(): MegolmBackupAuthData {
return MegolmBackupAuthData(
publicKey = "abcdefg",
signatures = HashMap<String, Map<String, String>>().apply {
this["something"] = HashMap<String, String>().apply {
this["ed25519:something"] = "hijklmnop"
}
}
signatures = mapOf("something" to mapOf("ed25519:something" to "hijklmnop"))
)
}
fun createFakeMegolmBackupCreationInfo(): MegolmBackupCreationInfo {
return MegolmBackupCreationInfo().apply {
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
authData = createFakeMegolmBackupAuthData()
}
return MegolmBackupCreationInfo(
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP,
authData = createFakeMegolmBackupAuthData()
)
}
}

View File

@@ -16,7 +16,10 @@
package im.vector.matrix.android.common
import org.junit.Assert.*
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

View File

@@ -22,11 +22,11 @@ object TestConstants {
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
// Time out to use when waiting for server response. 60s
private const val AWAIT_TIME_OUT_MILLIS = 60000
// 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 * 60000
private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000
const val USER_ALICE = "Alice"
const val USER_BOB = "Bob"

View File

@@ -22,7 +22,9 @@ 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.*
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

View File

@@ -21,7 +21,11 @@ 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 io.realm.Realm
import org.junit.Assert.*
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

View File

@@ -17,7 +17,9 @@
package im.vector.matrix.android.internal.crypto
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.*
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

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.crosssigning
import org.amshove.kluent.shouldBeNull
import org.amshove.kluent.shouldBeTrue
import org.junit.Test
@Suppress("SpellCheckingInspection")
class ExtensionsKtTest {
@Test
fun testComparingBase64StringWithOrWithoutPadding() {
// Without padding
"NMJyumnhMic".fromBase64().contentEquals("NMJyumnhMic".fromBase64()).shouldBeTrue()
// With padding
"NMJyumnhMic".fromBase64().contentEquals("NMJyumnhMic=".fromBase64()).shouldBeTrue()
}
@Test
fun testBadBase64() {
"===".fromBase64Safe().shouldBeNull()
}
}

View File

@@ -2,12 +2,10 @@ 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.api.MatrixCallback
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.common.TestMatrixCallback
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
@@ -21,7 +19,6 @@ 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)
@@ -34,16 +31,15 @@ class XSigningTest : InstrumentedTest {
fun test_InitializeAndStoreKeys() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val aliceLatch = CountDownLatch(1)
aliceSession.getCrossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
), TestMatrixCallback(aliceLatch))
mTestHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
), it)
}
mTestHelper.await(aliceLatch)
val myCrossSigningKeys = aliceSession.getCrossSigningService().getMyCrossSigningKeys()
val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys()
val masterPubKey = myCrossSigningKeys?.masterKey()
assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey)
val selfSigningKey = myCrossSigningKeys?.selfSigningKey()
@@ -53,9 +49,9 @@ class XSigningTest : InstrumentedTest {
assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted() == true)
assertTrue("Signing Keys should be trusted", aliceSession.getCrossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
mTestHelper.signout(aliceSession)
mTestHelper.signOutAndClose(aliceSession)
}
@Test
@@ -74,30 +70,24 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD
)
val latch = CountDownLatch(2)
aliceSession.getCrossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
bobSession.getCrossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
mTestHelper.await(latch)
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 downloadLatch = CountDownLatch(1)
aliceSession.downloadKeys(listOf(bobSession.myUserId), true, TestMatrixCallback(downloadLatch))
mTestHelper.await(downloadLatch)
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
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.getCrossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey)
assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey)
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.signout(aliceSession)
mTestHelper.signout(bobSession)
mTestHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession)
}
@Test
@@ -116,94 +106,56 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD
)
val latch = CountDownLatch(2)
aliceSession.getCrossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
bobSession.getCrossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
mTestHelper.await(latch)
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 downloadLatch = CountDownLatch(1)
val bobUserId = bobSession.myUserId
aliceSession.downloadKeys(listOf(bobUserId), true, TestMatrixCallback(downloadLatch))
mTestHelper.await(downloadLatch)
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().getUserCrossSigningKeys(bobUserId)
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
val trustLatch = CountDownLatch(1)
aliceSession.getCrossSigningService().trustUser(bobUserId, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
trustLatch.countDown()
}
override fun onFailure(failure: Throwable) {
fail("Failed to trust bob")
}
})
mTestHelper.await(trustLatch)
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
val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId!!
// Check that bob first session sees the new login
val bobKeysLatch = CountDownLatch(1)
bobSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>> {
override fun onFailure(failure: Throwable) {
fail("Failed to get device")
}
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
}
override fun onSuccess(data: MXUsersDevicesMap<CryptoDeviceInfo>) {
if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId!!) == false) {
fail("Bob should see the new device")
}
bobKeysLatch.countDown()
}
})
mTestHelper.await(bobKeysLatch)
if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
fail("Bob should see the new device")
}
val bobSecondDevicePOVFirstDevice = bobSession.getDeviceInfo(bobUserId, bobSecondDeviceId)
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
val bobSignLatch = CountDownLatch(1)
bobSession.getCrossSigningService().signDevice(bobSecondDeviceId!!, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
bobSignLatch.countDown()
}
override fun onFailure(failure: Throwable) {
fail("Failed to trust bob ${failure.localizedMessage}")
}
})
mTestHelper.await(bobSignLatch)
mTestHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
}
// Now alice should cross trust bob's second device
val aliceKeysLatch = CountDownLatch(1)
aliceSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>> {
override fun onFailure(failure: Throwable) {
fail("Failed to get device")
}
val data2 = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
}
override fun onSuccess(data: MXUsersDevicesMap<CryptoDeviceInfo>) {
// check that the device is seen
if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
fail("Alice should see the new device")
}
aliceKeysLatch.countDown()
}
})
mTestHelper.await(aliceKeysLatch)
// check that the device is seen
if (data2.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) {
fail("Alice should see the new device")
}
val result = aliceSession.getCrossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
mTestHelper.signout(aliceSession)
mTestHelper.signout(bobSession)
mTestHelper.signout(bobSession2)
mTestHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession)
mTestHelper.signOutAndClose(bobSession2)
}
}

View File

@@ -20,7 +20,9 @@ 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.*
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

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

@@ -19,21 +19,20 @@ 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.sas.CancelCode
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.SasMode
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
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
@@ -62,24 +61,20 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val bobTxCreatedLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
bobTxCreatedLatch.countDown()
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener)
val txID = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS,
bobSession.myUserId,
bobSession.getMyDevice().deviceId,
bobSession.cryptoService().getMyDevice().deviceId,
null)
assertNotNull("Alice should have a started transaction", txID)
@@ -106,9 +101,7 @@ class SASTest : InstrumentedTest {
// Let's cancel from alice side
val cancelLatch = CountDownLatch(1)
val bobListener2 = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
val bobListener2 = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.transactionId == txID) {
val immutableState = (tx as SASDefaultVerificationTransaction).state
@@ -117,8 +110,6 @@ class SASTest : InstrumentedTest {
}
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener2)
@@ -140,7 +131,7 @@ class SASTest : InstrumentedTest {
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
cryptoTestData.close()
cryptoTestData.cleanUp(mTestHelper)
}
@Test
@@ -157,19 +148,15 @@ class SASTest : InstrumentedTest {
var cancelReason: CancelCode? = null
val cancelLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobSession.getVerificationService().addListener(bobListener)
bobSession.cryptoService().verificationService().addListener(bobListener)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
@@ -184,20 +171,16 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.getMyDevice().deviceId
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceSession.getVerificationService().addListener(aliceListener)
aliceSession.cryptoService().verificationService().addListener(aliceListener)
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
@@ -205,7 +188,7 @@ class SASTest : InstrumentedTest {
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
cryptoTestData.close()
cryptoTestData.cleanUp(mTestHelper)
}
@Test
@@ -234,7 +217,7 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.getMyDevice().deviceId
val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
@@ -243,7 +226,7 @@ class SASTest : InstrumentedTest {
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.close()
cryptoTestData.cleanUp(mTestHelper)
}
@Test
@@ -272,7 +255,7 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.getMyDevice().deviceId
val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
@@ -281,7 +264,7 @@ class SASTest : InstrumentedTest {
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.close()
cryptoTestData.cleanUp(mTestHelper)
}
private fun fakeBobStart(bobSession: Session,
@@ -293,9 +276,9 @@ class SASTest : InstrumentedTest {
mac: List<String> = SASDefaultVerificationTransaction.KNOWN_MACS,
codes: List<String> = SASDefaultVerificationTransaction.KNOWN_SHORT_CODES) {
val startMessage = KeyVerificationStart(
fromDevice = bobSession.getMyDevice().deviceId,
fromDevice = bobSession.cryptoService().getMyDevice().deviceId,
method = VerificationMethod.SAS.toValue(),
transactionID = tid,
transactionId = tid,
keyAgreementProtocols = protocols,
hashes = hashes,
messageAuthenticationCodes = mac,
@@ -323,12 +306,12 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val aliceCreatedLatch = CountDownLatch(2)
val aliceCancelledLatch = CountDownLatch(2)
val createdTx = mutableListOf<SASDefaultVerificationTransaction>()
val aliceListener = object : VerificationService.VerificationListener {
val aliceListener = object : VerificationService.Listener {
override fun transactionCreated(tx: VerificationTransaction) {
createdTx.add(tx as SASDefaultVerificationTransaction)
aliceCreatedLatch.countDown()
@@ -339,20 +322,18 @@ class SASTest : InstrumentedTest {
aliceCancelledLatch.countDown()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceVerificationService.addListener(aliceListener)
val bobUserId = bobSession!!.myUserId
val bobDeviceId = bobSession.getMyDevice().deviceId
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.close()
cryptoTestData.cleanUp(mTestHelper)
}
/**
@@ -365,61 +346,53 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
var accepted: KeyVerificationAccept? = null
var startReq: KeyVerificationStart? = null
var accepted: ValidVerificationInfoAccept? = null
var startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null
val aliceAcceptedLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.VerificationListener {
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
override fun transactionCreated(tx: VerificationTransaction) {}
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
accepted = at.accepted
startReq = at.startReq
aliceAcceptedLatch.countDown()
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.getMyDevice().deviceId
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))
assertTrue("Agreed Protocol should be Valid", accepted != null)
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))
accepted!!.shortAuthenticationStrings.forEach {
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
}
cryptoTestData.close()
cryptoTestData.cleanUp(mTestHelper)
}
@Test
@@ -429,13 +402,11 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
when (uxState) {
@@ -445,15 +416,11 @@ class SASTest : InstrumentedTest {
else -> Unit
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceVerificationService.addListener(aliceListener)
val bobSASLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState
when (uxState) {
@@ -466,13 +433,11 @@ class SASTest : InstrumentedTest {
bobSASLatch.countDown()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.getMyDevice().deviceId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceSASLatch)
mTestHelper.await(bobSASLatch)
@@ -483,7 +448,7 @@ class SASTest : InstrumentedTest {
assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
cryptoTestData.close()
cryptoTestData.cleanUp(mTestHelper)
}
@Test
@@ -493,13 +458,11 @@ class SASTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val aliceVerificationService = aliceSession.cryptoService().verificationService()
val bobVerificationService = bobSession!!.cryptoService().verificationService()
val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
val aliceListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
when (uxState) {
@@ -512,15 +475,11 @@ class SASTest : InstrumentedTest {
else -> Unit
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceVerificationService.addListener(aliceListener)
val bobSASLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
val bobListener = object : VerificationService.Listener {
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState
when (uxState) {
@@ -536,26 +495,24 @@ class SASTest : InstrumentedTest {
else -> Unit
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.getMyDevice().deviceId
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.getDeviceInfo(bobUserId, bobDeviceId)
val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.getDeviceInfo(aliceSession.myUserId, aliceSession.getMyDevice().deviceId)
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.close()
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

@@ -1,5 +1,5 @@
/*
* Copyright 2020 New Vector Ltd
* 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.
@@ -32,14 +32,14 @@ class SharedSecretTest : InstrumentedTest {
@Test
fun testSharedSecretLengthCase() {
repeat(100) {
generateSharedSecret().length shouldBe 43
generateSharedSecretV2().length shouldBe 11
}
}
@Test
fun testSharedDiffCase() {
val sharedSecret1 = generateSharedSecret()
val sharedSecret2 = generateSharedSecret()
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.api.session.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

@@ -20,15 +20,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.database.helper.add
import im.vector.matrix.android.internal.database.helper.lastStateIndex
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
@@ -58,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
}
}
@@ -68,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
}
}
@@ -140,9 +101,9 @@ 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()
}
@@ -155,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)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
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
}
}
@@ -169,19 +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)
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.nextToken shouldEqual nextToken
}
}
private fun ChunkEntity.addAll(roomId: String,
events: List<Event>,
direction: PaginationDirection,
stateIndexOffset: Int = 0) {
direction: PaginationDirection) {
events.forEach { event ->
add(roomId, event, direction, stateIndexOffset)
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

@@ -23,6 +23,7 @@ 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
@@ -35,7 +36,8 @@ 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 {
@@ -57,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

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

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

@@ -16,7 +16,6 @@
package im.vector.matrix.android.api.extensions
import im.vector.matrix.android.api.comparators.DatedObjectComparators
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
@@ -33,7 +32,5 @@ fun CryptoDeviceInfo.getFingerprintHumanReadable() = fingerprint()
* ========================================================================================== */
fun List<DeviceInfo>.sortByLastSeen(): List<DeviceInfo> {
val list = toMutableList()
list.sortWith(DatedObjectComparators.descComparator)
return list
return this.sortedByDescending { it.lastSeenTs ?: 0 }
}

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

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

@@ -17,25 +17,24 @@ package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.matrix.android.api.pushrules.rest.RuleSet
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.util.Cancelable
interface PushRuleService {
/**
* Fetch the push rules from the server
*/
fun fetchPushRules(scope: String = RuleScope.GLOBAL)
// TODO get push rule set
fun getPushRules(scope: String = RuleScope.GLOBAL): List<PushRule>
// TODO update rule
fun getPushRules(scope: String = RuleScope.GLOBAL): RuleSet
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
fun addPushRuleListener(listener: PushRuleListener)

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

@@ -19,10 +19,18 @@ import im.vector.matrix.android.api.session.events.model.Event
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 {

View File

@@ -20,18 +20,19 @@ import com.squareup.moshi.JsonClass
/**
* All push rulesets for a user.
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
*/
@JsonClass(generateAdapter = true)
data class GetPushRulesResponse(
internal data class GetPushRulesResponse(
/**
* Global rules, account level applying to all devices
*/
@Json(name = "global")
val global: Ruleset,
val global: RuleSet,
/**
* Device specific rules, apply only to current device
*/
@Json(name = "device")
val device: Ruleset? = null
val device: RuleSet? = null
)

View File

@@ -17,60 +17,76 @@ 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
/**
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
*/
@JsonClass(generateAdapter = true)
data class PushCondition(
/**
* Required. The kind of condition to apply.
*/
@Json(name = "kind")
val kind: String,
/**
* Required for event_match conditions. The dot- separated field of the event to match.
*/
@Json(name = "key")
val key: String? = null,
/**
*Required for event_match conditions.
*/
/**
* Required for event_match conditions.
*/
@Json(name = "pattern")
val pattern: String? = null,
/**
* Required for room_member_count conditions.
* 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 ==.
*/
@Json(name = "is") val iz: String? = null
@Json(name = "is")
val iz: String? = null
) {
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,31 +18,158 @@ 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.Action
import im.vector.matrix.android.api.pushrules.getActions
import im.vector.matrix.android.api.pushrules.toJson
/**
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
*/
@JsonClass(generateAdapter = true)
data class PushRule(
/**
* Required. The actions to perform when this rule is matched.
*/
@Json(name = "actions")
val actions: List<Any>,
/**
* Required. Whether this is a default rule, or has been set explicitly.
*/
@Json(name = "default")
val default: Boolean? = false,
/**
* Required. Whether the push rule is enabled or not.
*/
@Json(name = "enabled")
val enabled: Boolean,
/**
* Required. The ID of this rule.
*/
@Json(name = "rule_id") val ruleId: String,
@Json(name = "rule_id")
val ruleId: String,
/**
* The conditions that must hold true for an event in order for a rule to be applied to an event
*/
@Json(name = "conditions")
val conditions: List<PushCondition>? = null,
/**
* The glob-style pattern to match against. Only applicable to content rules.
*/
@Json(name = "pattern")
val pattern: String? = null
)
) {
/**
* Add the default notification sound.
*/
fun setNotificationSound(): PushRule {
return setNotificationSound(ACTION_VALUE_DEFAULT)
}
fun getNotificationSound(): String? {
return (getActions().firstOrNull { it is Action.Sound } as? Action.Sound)?.sound
}
/**
* Set the notification sound
*
* @param sound notification sound
*/
fun setNotificationSound(sound: String): PushRule {
return copy(
actions = (getActions().filter { it !is Action.Sound } + Action.Sound(sound)).toJson()
)
}
/**
* Remove the notification sound
*/
fun removeNotificationSound(): PushRule {
return copy(
actions = getActions().filter { it !is Action.Sound }.toJson()
)
}
/**
* Set the highlight status.
*
* @param highlight the highlight status
*/
fun setHighlight(highlight: Boolean): PushRule {
return copy(
actions = (getActions().filter { it !is Action.Highlight } + Action.Highlight(highlight)).toJson()
)
}
/**
* Set the notification status.
*
* @param notify true to notify
*/
fun setNotify(notify: Boolean): PushRule {
val mutableActions = actions.toMutableList()
mutableActions.remove(ACTION_DONT_NOTIFY)
mutableActions.remove(ACTION_NOTIFY)
if (notify) {
mutableActions.add(ACTION_NOTIFY)
} else {
mutableActions.add(ACTION_DONT_NOTIFY)
}
return copy(actions = mutableActions)
}
/**
* Return true if the rule should highlight the event.
*
* @return true if the rule should play sound
*/
fun shouldNotify() = actions.contains(ACTION_NOTIFY)
/**
* Return true if the rule should not highlight the event.
*
* @return true if the rule should not play sound
*/
fun shouldNotNotify() = actions.contains(ACTION_DONT_NOTIFY)
companion object {
/* ==========================================================================================
* Rule id
* ========================================================================================== */
const val RULE_ID_DISABLE_ALL = ".m.rule.master"
const val RULE_ID_CONTAIN_USER_NAME = ".m.rule.contains_user_name"
const val RULE_ID_CONTAIN_DISPLAY_NAME = ".m.rule.contains_display_name"
const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one"
const val RULE_ID_INVITE_ME = ".m.rule.invite_for_me"
const val RULE_ID_PEOPLE_JOIN_LEAVE = ".m.rule.member_event"
const val RULE_ID_CALL = ".m.rule.call"
const val RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS = ".m.rule.suppress_notices"
const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
const val RULE_ID_AT_ROOMS = ".m.rule.roomnotif"
const val RULE_ID_TOMBSTONE = ".m.rule.tombstone"
const val RULE_ID_E2E_ONE_TO_ONE_ROOM = ".m.rule.encrypted_room_one_to_one"
const val RULE_ID_E2E_GROUP = ".m.rule.encrypted"
const val RULE_ID_REACTION = ".m.rule.reaction"
const val RULE_ID_FALLBACK = ".m.rule.fallback"
/* ==========================================================================================
* Actions
* ========================================================================================== */
const val ACTION_NOTIFY = "notify"
const val ACTION_DONT_NOTIFY = "dont_notify"
const val ACTION_COALESCE = "coalesce"
const val ACTION_SET_TWEAK_SOUND_VALUE = "sound"
const val ACTION_SET_TWEAK_HIGHLIGHT_VALUE = "highlight"
const val ACTION_PARAMETER_SET_TWEAK = "set_tweak"
const val ACTION_PARAMETER_VALUE = "value"
const val ACTION_VALUE_DEFAULT = "default"
const val ACTION_VALUE_RING = "ring"
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.pushrules.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.pushrules.RuleSetKey
/**
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
*/
@JsonClass(generateAdapter = true)
data class RuleSet(
@Json(name = "content")
val content: List<PushRule>? = null,
@Json(name = "override")
val override: List<PushRule>? = null,
@Json(name = "room")
val room: List<PushRule>? = null,
@Json(name = "sender")
val sender: List<PushRule>? = null,
@Json(name = "underride")
val underride: List<PushRule>? = null
) {
fun getAllRules(): List<PushRule> {
// Ref. for the order: https://matrix.org/docs/spec/client_server/latest#push-rules
return override.orEmpty() + content.orEmpty() + room.orEmpty() + sender.orEmpty() + underride.orEmpty()
}
/**
* Find a rule from its ruleID.
*
* @param ruleId a RULE_ID_XX value
* @return the matched bing rule or null it doesn't exist.
*/
fun findDefaultRule(ruleId: String?): PushRuleAndKind? {
var result: PushRuleAndKind? = null
// sanity check
if (null != ruleId) {
if (PushRule.RULE_ID_CONTAIN_USER_NAME == ruleId) {
result = findRule(content, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.CONTENT) }
} else {
// assume that the ruleId is unique.
result = findRule(override, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.OVERRIDE) }
if (null == result) {
result = findRule(underride, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.UNDERRIDE) }
}
}
}
return result
}
/**
* Find a rule from its rule Id.
*
* @param rules the rules list.
* @param ruleId the rule Id.
* @return the bing rule if it exists, else null.
*/
private fun findRule(rules: List<PushRule>?, ruleId: String): PushRule? {
return rules?.firstOrNull { it.ruleId == ruleId }
}
}
data class PushRuleAndKind(
val pushRule: PushRule,
val kind: RuleSetKey
)

View File

@@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.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
@@ -33,6 +34,7 @@ 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
@@ -47,7 +49,6 @@ interface Session :
RoomDirectoryService,
GroupService,
UserService,
CryptoService,
CacheService,
SignOutService,
FilterService,
@@ -57,7 +58,8 @@ interface Session :
PushersService,
InitialSyncProgressService,
HomeServerCapabilitiesService,
SecureStorageService {
SecureStorageService,
AccountDataService {
/**
* The params associated to the session
@@ -136,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.
@@ -159,4 +166,6 @@ interface Session :
*/
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

@@ -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?,
private val mimeType: String?,
val type: Type
) : Parcelable {
@@ -40,4 +41,6 @@ data class ContentAttachmentData(
AUDIO,
VIDEO
}
fun getSafeMimeType() = if (mimeType == "image/jpg") "image/jpeg" else mimeType
}

View File

@@ -23,7 +23,7 @@ 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.VerificationService
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
@@ -40,6 +40,12 @@ 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>)
@@ -50,12 +56,6 @@ interface CryptoService {
fun isCryptoEnabled(): Boolean
fun getVerificationService(): VerificationService
fun getCrossSigningService(): CrossSigningService
fun getKeysBackupService(): KeysBackupService
fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean
fun setWarnOnUnknownDevices(warn: Boolean)

View File

@@ -18,7 +18,6 @@ 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.crypto.RoomEncryptionTrustLevel
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
@@ -43,6 +42,10 @@ interface CrossSigningService {
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>>
@@ -54,15 +57,15 @@ interface CrossSigningService {
fun trustUser(otherUserId: String,
callback: MatrixCallback<Unit>)
fun markMyMasterKeyAsTrusted()
/**
* Sign one of your devices and upload the signature
*/
fun signDevice(deviceId: String,
callback: MatrixCallback<Unit>)
fun trustDevice(deviceId: String,
callback: MatrixCallback<Unit>)
fun checkDeviceTrust(otherUserId: String,
otherDeviceId: String,
locallyTrusted: Boolean?): DeviceTrustResult
fun getTrustLevelForUsers(userIds: List<String>): RoomEncryptionTrustLevel
}

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

@@ -14,8 +14,7 @@
* limitations under the License.
*/
// TODO Rename package
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"),

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,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
interface IncomingSasVerificationTransaction : SasVerificationTransaction {
val uxState: UxState

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
interface OutgoingSasVerificationTransaction : SasVerificationTransaction {
val uxState: UxState

View File

@@ -0,0 +1,80 @@
/*
* 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.verification
import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
import java.util.UUID
/**
* Stores current pending verification requests
*/
data class PendingVerificationRequest(
val ageLocalTs: Long,
val isIncoming: Boolean = false,
val localId: String = UUID.randomUUID().toString(),
val otherUserId: String,
val roomId: String?,
val transactionId: String? = null,
val requestInfo: ValidVerificationInfoRequest? = null,
val readyInfo: ValidVerificationInfoReady? = null,
val cancelConclusion: CancelCode? = null,
val isSuccessful: Boolean = false,
val handledByOtherSession: Boolean = false,
// In case of to device it is sent to a list of devices
val targetDevices: List<String>? = null
) {
val isReady: Boolean = readyInfo != null
val isSent: Boolean = transactionId != null
val isFinished: Boolean = isSuccessful || cancelConclusion != null
/**
* SAS is supported if I support it and the other party support it
*/
fun isSasSupported(): Boolean {
return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
}
/**
* Other can show QR code if I can scan QR code and other can show QR code
*/
fun otherCanShowQrCode(): Boolean {
return if (isIncoming) {
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
} else {
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
}
}
/**
* Other can scan QR code if I can show QR code and other can scan QR code
*/
fun otherCanScanQrCode(): Boolean {
return if (isIncoming) {
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
} else {
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
&& readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
}
}
}

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
interface QrCodeVerificationTransaction : VerificationTransaction {

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
object SasMode {
const val DECIMAL = "decimal"

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
interface SasVerificationTransaction : VerificationTransaction {

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.verification
data class ValidVerificationInfoReady(
val transactionId: String,
val fromDevice: String,
val methods: List<String>
)

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.verification
data class ValidVerificationInfoRequest(
val transactionId: String,
val fromDevice: String,
val methods: List<String>,
val timestamp: Long?
)

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
/**
* Verification methods

View File

@@ -14,11 +14,10 @@
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
package im.vector.matrix.android.api.session.crypto.verification
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
/**
* https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework
@@ -30,9 +29,9 @@ import im.vector.matrix.android.internal.crypto.verification.PendingVerification
*/
interface VerificationService {
fun addListener(listener: VerificationListener)
fun addListener(listener: Listener)
fun removeListener(listener: VerificationListener)
fun removeListener(listener: Listener)
/**
* Mark this device as verified manually
@@ -68,11 +67,11 @@ interface VerificationService {
otherDevices: List<String>?): PendingVerificationRequest
fun declineVerificationRequestInDMs(otherUserId: String,
otherDeviceId: String,
transactionId: String,
roomId: String)
// Only SAS method is supported for the moment
// TODO Parameter otherDeviceId should be removed in this case
fun beginKeyVerificationInDMs(method: VerificationMethod,
transactionId: String,
roomId: String,
@@ -95,15 +94,33 @@ interface VerificationService {
otherUserId: String,
transactionId: String): Boolean
// fun transactionUpdated(tx: SasVerificationTransaction)
interface VerificationListener {
fun transactionCreated(tx: VerificationTransaction)
fun transactionUpdated(tx: VerificationTransaction)
fun markedAsManuallyVerified(userId: String, deviceId: String) {}
interface Listener {
/**
* Called when a verification request is created either by the user, or by the other user.
*/
fun verificationRequestCreated(pr: PendingVerificationRequest) {}
/**
* Called when a verification request is updated.
*/
fun verificationRequestUpdated(pr: PendingVerificationRequest) {}
/**
* Called when a transaction is created, either by the user or initiated by the other user.
*/
fun transactionCreated(tx: VerificationTransaction) {}
/**
* Called when a transaction is updated. You may be interested to track the state of the VerificationTransaction.
*/
fun transactionUpdated(tx: VerificationTransaction) {}
/**
* Inform the the deviceId of the userId has been marked as manually verified by the SDK.
* It will be called after VerificationService.markedLocallyAsManuallyVerified() is called.
*
*/
fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
companion object {

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
interface VerificationTransaction {

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
sealed class VerificationTxState {
// Uninitialized state

View File

@@ -157,6 +157,11 @@ data class Event(
*/
fun isRedacted() = unsignedData?.redactedEvent != null
/**
* Tells if the event is redacted by the user himself.
*/
fun isRedactedBySameUser() = senderId == unsignedData?.redactedEvent?.senderId
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -200,7 +205,7 @@ data class Event(
fun Event.isTextMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.type) {
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
MessageType.MSGTYPE_TEXT,
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_NOTICE -> true
@@ -210,7 +215,7 @@ fun Event.isTextMessage(): Boolean {
fun Event.isImageMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.type) {
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
MessageType.MSGTYPE_IMAGE -> true
else -> false
}

View File

@@ -66,6 +66,9 @@ object EventType {
const val ROOM_KEY_REQUEST = "m.room_key_request"
const val FORWARDED_ROOM_KEY = "m.forwarded_room_key"
const val REQUEST_SECRET = "m.secret.request"
const val SEND_SECRET = "m.secret.send"
// Interactive key verification
const val KEY_VERIFICATION_START = "m.key.verification.start"
const val KEY_VERIFICATION_ACCEPT = "m.key.verification.accept"

View File

@@ -20,7 +20,7 @@ import java.util.UUID
object LocalEcho {
private const val PREFIX = "local."
private const val PREFIX = "\$local."
fun isLocalEchoId(eventId: String) = eventId.startsWith(PREFIX)

View File

@@ -19,11 +19,12 @@ package im.vector.matrix.android.api.session.events.model
* Constants defining known event relation types from Matrix specifications
*/
object RelationType {
/** Lets you define an event which annotates an existing event.*/
const val ANNOTATION = "m.annotation"
/** Lets you define an event which replaces an existing event.*/
const val REPLACE = "m.replace"
/** Lets you define an event which references an existing event.*/
const val REFERENCE = "m.reference"
/** Lets you define an event which adds a response to an existing event.*/
const val RESPONSE = "m.response"
}

View File

@@ -34,7 +34,11 @@ interface FileService {
/**
* Download file in cache
*/
FOR_INTERNAL_USE
FOR_INTERNAL_USE,
/**
* Download file in file provider path
*/
FOR_EXTERNAL_SHARE
}
/**

View File

@@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.pushers
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import java.util.UUID
interface PushersService {
@@ -28,19 +29,32 @@ interface PushersService {
/**
* Add a new HTTP pusher.
* Note that only `http` kind is supported by the SDK for now.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
*
* @param pushkey the pushkey
* @param pushkey This is a unique identifier for this pusher. The value you should use for
* this is the routing or destination address information for the notification,
* for example, the APNS token for APNS or the Registration ID for GCM. If your
* notification client has no such concept, use any unique identifier. Max length, 512 chars.
* If the kind is "email", this is the email address to send notifications to.
* @param appId the application id
* @param profileTag the profile tag
* @param lang the language
* @param appDisplayName a human-readable application name
* @param deviceDisplayName a human-readable device name
* @param url the URL that should be used to send notifications
* @param append append the pusher
* @param withEventIdOnly true to limit the push content
* This is a reverse-DNS style identifier for the application. It is recommended
* that this end with the platform, such that different platform versions get
* different app identifiers. Max length, 64 chars.
* @param profileTag This string determines which set of device specific rules this pusher executes.
* @param lang The preferred language for receiving notifications (e.g. "en" or "en-US").
* @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher.
* @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher.
* @param url The URL to use to send notifications to. MUST be an HTTPS URL with a path of /_matrix/push/v1/notify.
* @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
* with the same App ID and pushkey for different users.
* @param withEventIdOnly true to limit the push content to only id and not message content
* Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#homeserver-behaviour
*
* @return A work request uuid. Can be used to listen to the status
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
* @return A work request uuid. Can be used to listen to the status
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
* @throws [InvalidParameterException] if a parameter is not correct
*/
fun addHttpPusher(pushkey: String,
appId: String,
@@ -52,13 +66,18 @@ interface PushersService {
append: Boolean,
withEventIdOnly: Boolean): UUID
fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback<Unit>)
companion object {
const val EVENT_ID_ONLY = "event_id_only"
}
/**
* Remove the http pusher
*/
fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback<Unit>): Cancelable
/**
* Get the current pushers, as a LiveData
*/
fun getPushersLive(): LiveData<List<Pusher>>
fun pushers() : List<Pusher>
/**
* Get the current pushers
*/
fun getPushers(): List<Pusher>
}

View File

@@ -35,9 +35,9 @@ interface RoomDirectoryService {
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
/**
* Join a room by id
* Join a room by id, or room alias
*/
fun joinRoom(roomId: String,
fun joinRoom(roomIdOrAlias: String,
reason: String? = null,
callback: MatrixCallback<Unit>): Cancelable

View File

@@ -36,11 +36,11 @@ interface RoomService {
/**
* Join a room by id
* @param roomId the roomId of the room to join
* @param roomIdOrAlias the roomId or the room alias of the room to join
* @param reason optional reason for joining the room
* @param viaServers the servers to attempt to join the room through. One of the servers must be participating in the room.
*/
fun joinRoom(roomId: String,
fun joinRoom(roomIdOrAlias: String,
reason: String? = null,
viaServers: List<String> = emptyList(),
callback: MatrixCallback<Unit>): Cancelable

View File

@@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.room.crypto
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
interface RoomCryptoService {
@@ -26,5 +27,9 @@ interface RoomCryptoService {
fun shouldEncryptForInvitedMembers(): Boolean
fun enableEncryptionWithAlgorithm(algorithm: String, callback: MatrixCallback<Unit>)
/**
* Enable encryption of the room
*/
fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM,
callback: MatrixCallback<Unit>)
}

View File

@@ -19,5 +19,6 @@ data class EventAnnotationsSummary(
var eventId: String,
var reactionsSummary: List<ReactionAggregatedSummary>,
var editSummary: EditAggregatedSummary?,
var pollResponseSummary: PollResponseAggregatedSummary?,
var referencesAggregatedSummary: ReferencesAggregatedSummary? = null
)

View File

@@ -0,0 +1,29 @@
/*
* 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.room.model
data class PollResponseAggregatedSummary(
var aggregatedContent: PollSummaryContent? = null,
// If set the poll is closed (Clients SHOULD NOT consider responses after the close event)
var closedTime: Long? = null,
// Clients SHOULD validate that the option in the relationship is a valid option, and ignore the response if invalid
var nbOptions: Int = 0,
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
val sourceEvents: List<String>,
val localEchos: List<String>
)

View File

@@ -0,0 +1,48 @@
/*
* 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.room.model
import com.squareup.moshi.JsonClass
/**
* Contains an aggregated summary info of the poll response.
* Put pre-computed info that you want to access quickly without having
* to go through all references events
*/
@JsonClass(generateAdapter = true)
data class PollSummaryContent(
// Index of my vote
var myVote: Int? = null,
// Array of VoteInfo, list is constructed so that there is only one vote by user
// And that optionIndex is valid
var votes: List<VoteInfo>? = null
) {
fun voteCount(): Int {
return votes?.size ?: 0
}
fun voteCountForOption(optionIndex: Int) : Int {
return votes?.filter { it.optionIndex == optionIndex }?.count() ?: 0
}
}
@JsonClass(generateAdapter = true)
data class VoteInfo(
val userId: String,
val optionIndex: Int,
val voteTimestamp: Long
)

View File

@@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.session.room.VerificationState
/**
* Contains an aggregated summary info of the references.
@@ -26,6 +27,6 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class ReferencesAggregatedContent(
// Verification status info for m.key.verification.request msgType events
@Json(name = "verif_sum") val verificationSummary: String
@Json(name = "verif_sum") val verificationState: VerificationState
// Add more fields for future summary info.
)

View File

@@ -18,9 +18,28 @@ package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json
/**
* Ref: https://matrix.org/docs/spec/client_server/latest#room-history-visibility
*/
enum class RoomHistoryVisibility {
/**
* All events while this is the m.room.history_visibility value may be shared by any
* participating homeserver with anyone, regardless of whether they have ever joined the room.
*/
@Json(name = "world_readable") WORLD_READABLE,
/**
* Previous events are always accessible to newly joined members. All events in the
* room are accessible, even those sent when the member was not a part of the room.
*/
@Json(name = "shared") SHARED,
/**
* Events are accessible to newly joined members from the point they were invited onwards.
* Events stop being accessible when the member's state changes to something other than invite or join.
*/
@Json(name = "invited") INVITED,
@Json(name = "joined") JOINED,
@Json(name = "world_readable") WORLD_READABLE
/**
* Events are accessible to newly joined members from the point they joined the room onwards.
* Events stop being accessible when the member's state changes to something other than join.
*/
@Json(name = "joined") JOINED
}

View File

@@ -16,8 +16,6 @@
package im.vector.matrix.android.api.session.room.model
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
/**
* Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content
*/
@@ -25,7 +23,5 @@ data class RoomMemberSummary constructor(
val membership: Membership,
val userId: String,
val displayName: String? = null,
val avatarUrl: String? = null,
// TODO Warning: Will not be populated if not using RxRoom
val userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
val avatarUrl: String? = null
)

View File

@@ -45,7 +45,8 @@ data class RoomSummary constructor(
val versioningState: VersioningState = VersioningState.NONE,
val readMarkerId: String? = null,
val userDrafts: List<UserDraft> = emptyList(),
var isEncrypted: Boolean,
val isEncrypted: Boolean,
val inviterId: String? = null,
val typingRoomMemberIds: List<String> = emptyList(),
val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
// TODO Plug it

View File

@@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.room.model.create
import android.util.Patterns
import androidx.annotation.CheckResult
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.MatrixPatterns.isUserId
@@ -120,37 +121,53 @@ data class CreateRoomParams(
@Json(name = "power_level_content_override")
val powerLevelContentOverride: PowerLevelsContent? = null
) {
/**
* Set to true means that if cross-signing is enabled and we can get keys for every invited users,
* the encryption will be enabled on the created room
*/
@Transient
internal var enableEncryptionIfInvitedUsersSupportIt: Boolean = false
private set
fun enableEncryptionIfInvitedUsersSupportIt(): CreateRoomParams {
enableEncryptionIfInvitedUsersSupportIt = true
/**
* After calling this method, when the room will be created, if cross-signing is enabled and we can get keys for every invited users,
* the encryption will be enabled on the created room
* @param value true to activate this behavior.
* @return this, to allow chaining methods
*/
fun enableEncryptionIfInvitedUsersSupportIt(value: Boolean = true): CreateRoomParams {
enableEncryptionIfInvitedUsersSupportIt = value
return this
}
/**
* Add the crypto algorithm to the room creation parameters.
*
* @param algorithm the algorithm
* @param enable true to enable encryption.
* @param algorithm the algorithm, default to [MXCRYPTO_ALGORITHM_MEGOLM], which is actually the only supported algorithm for the moment
* @return a modified copy of the CreateRoomParams object, or this if there is no modification
*/
fun enableEncryptionWithAlgorithm(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM): CreateRoomParams {
@CheckResult
fun enableEncryptionWithAlgorithm(enable: Boolean = true,
algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM): CreateRoomParams {
// Remove the existing value if any.
val newInitialStates = initialStates
?.filter { it.type != EventType.STATE_ROOM_ENCRYPTION }
return if (algorithm == MXCRYPTO_ALGORITHM_MEGOLM) {
val contentMap = mapOf("algorithm" to algorithm)
if (enable) {
val contentMap = mapOf("algorithm" to algorithm)
val algoEvent = Event(
type = EventType.STATE_ROOM_ENCRYPTION,
stateKey = "",
content = contentMap.toContent()
)
val algoEvent = Event(
type = EventType.STATE_ROOM_ENCRYPTION,
stateKey = "",
content = contentMap.toContent()
)
copy(
initialStates = initialStates.orEmpty().filter { it.type != EventType.STATE_ROOM_ENCRYPTION } + algoEvent
)
copy(
initialStates = newInitialStates.orEmpty() + algoEvent
)
} else {
return copy(
initialStates = newInitialStates
)
}
} else {
Timber.e("Unsupported algorithm: $algorithm")
this
@@ -161,7 +178,9 @@ data class CreateRoomParams(
* Force the history visibility in the room creation parameters.
*
* @param historyVisibility the expected history visibility, set null to remove any existing value.
* @return a modified copy of the CreateRoomParams object
*/
@CheckResult
fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?): CreateRoomParams {
// Remove the existing value if any.
val newInitialStates = initialStates
@@ -187,7 +206,9 @@ data class CreateRoomParams(
/**
* Mark as a direct message room.
* @return a modified copy of the CreateRoomParams object
*/
@CheckResult
fun setDirectMessage(): CreateRoomParams {
return copy(
preset = CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT,
@@ -195,20 +216,6 @@ data class CreateRoomParams(
)
}
/**
* @return the invite count
*/
private fun getInviteCount(): Int {
return invitedUserIds?.size ?: 0
}
/**
* @return the pid invite count
*/
private fun getInvite3PidCount(): Int {
return invite3pids?.size ?: 0
}
/**
* Tells if the created room can be a direct chat one.
*
@@ -217,7 +224,6 @@ data class CreateRoomParams(
fun isDirect(): Boolean {
return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT
&& isDirect == true
&& (1 == getInviteCount() || 1 == getInvite3PidCount())
}
/**
@@ -232,7 +238,9 @@ data class CreateRoomParams(
* ids might be a matrix id or an email address.
*
* @param ids the participant ids to add.
* @return a modified copy of the CreateRoomParams object
*/
@CheckResult
fun addParticipantIds(hsConfig: HomeServerConnectionConfig,
userId: String,
ids: List<String>): CreateRoomParams {

View File

@@ -21,5 +21,10 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class CreateRoomResponse(
@Json(name = "room_id") var roomId: String? = null
/**
* Required. The created room's ID.
*/
@Json(name = "room_id") val roomId: String
)
internal typealias JoinRoomResponse = CreateRoomResponse

View File

@@ -25,9 +25,9 @@ import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
@JsonClass(generateAdapter = true)
data class MessageAudioContent(
/**
* Not documented
* Required. Must be 'm.audio'.
*/
@Json(name = "msgtype") override val type: String,
@Json(name = "msgtype") override val msgType: String,
/**
* Required. A description of the audio e.g. 'Bee Gees - Stayin' Alive', or some kind of content description for accessibility e.g. 'audio attachment'.
@@ -40,7 +40,7 @@ data class MessageAudioContent(
@Json(name = "info") val audioInfo: AudioInfo? = null,
/**
* Required. Required if the file is not encrypted. The URL (typically MXC URI) to the audio clip.
* Required if the file is not encrypted. The URL (typically MXC URI) to the audio clip.
*/
@Json(name = "url") override val url: String? = null,
@@ -51,4 +51,4 @@ data class MessageAudioContent(
* Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.
*/
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
) : MessageEncryptedContent
) : MessageWithAttachmentContent

View File

@@ -20,8 +20,7 @@ import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
interface MessageContent {
// TODO Rename to msgType
val type: String
val msgType: String
val body: String
val relatesTo: RelationDefaultContent?
val newContent: Content?

View File

@@ -23,7 +23,7 @@ import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultC
@JsonClass(generateAdapter = true)
data class MessageDefaultContent(
@Json(name = "msgtype") override val type: String,
@Json(name = "msgtype") override val msgType: String,
@Json(name = "body") override val body: String,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null

View File

@@ -23,10 +23,26 @@ import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultC
@JsonClass(generateAdapter = true)
data class MessageEmoteContent(
@Json(name = "msgtype") override val type: String,
/**
* Required. Must be 'm.emote'.
*/
@Json(name = "msgtype") override val msgType: String,
/**
* Required. The emote action to perform.
*/
@Json(name = "body") override val body: String,
/**
* The format used in the formatted_body. Currently only org.matrix.custom.html is supported.
*/
@Json(name = "format") val format: String? = null,
/**
* The formatted version of the body. This is required if format is specified.
*/
@Json(name = "formatted_body") val formattedBody: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null
) : MessageContent

View File

@@ -26,9 +26,9 @@ import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
@JsonClass(generateAdapter = true)
data class MessageFileContent(
/**
* Not documented
* Required. Must be 'm.file'.
*/
@Json(name = "msgtype") override val type: String,
@Json(name = "msgtype") override val msgType: String,
/**
* Required. A human-readable description of the file. This is recommended to be the filename of the original upload.
@@ -46,15 +46,18 @@ data class MessageFileContent(
@Json(name = "info") val info: FileInfo? = null,
/**
* Required. Required if the file is unencrypted. The URL (typically MXC URI) to the file.
* Required if the file is unencrypted. The URL (typically MXC URI) to the file.
*/
@Json(name = "url") override val url: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null,
/**
* Required if the file is encrypted. Information on the encrypted file, as specified in End-to-end encryption.
*/
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
) : MessageEncryptedContent {
) : MessageWithAttachmentContent {
fun getMimeType(): String {
// Mimetype default to plain text, should not be used

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 New Vector Ltd
* 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.
@@ -14,10 +14,8 @@
* limitations under the License.
*/
package im.vector.riotx.features.home.room.detail
package im.vector.matrix.android.api.session.room.model.message
data class FileTooBigError(
val filename: String,
val fileSizeInBytes: Long,
val homeServerLimitInBytes: Long
)
object MessageFormat {
const val FORMAT_MATRIX_HTML = "org.matrix.custom.html"
}

View File

@@ -27,7 +27,7 @@ data class MessageImageContent(
/**
* Required. Must be 'm.image'.
*/
@Json(name = "msgtype") override val type: String,
@Json(name = "msgtype") override val msgType: String,
/**
* Required. A textual representation of the image. This could be the alt text of the image, the filename of the image,
@@ -41,7 +41,7 @@ data class MessageImageContent(
@Json(name = "info") override val info: ImageInfo? = null,
/**
* Required. Required if the file is unencrypted. The URL (typically MXC URI) to the image.
* Required if the file is unencrypted. The URL (typically MXC URI) to the image.
*/
@Json(name = "url") override val url: String? = null,

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