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

Compare commits

...

584 Commits

Author SHA1 Message Date
Benoit Marty
637eba277f Merge branch 'release/0.14.0' 2020-02-01 17:20:05 +01:00
Benoit Marty
e9f220cca2 Prepare release 0.14.0 2020-02-01 17:19:53 +01:00
Benoit Marty
8a9bd97a88 Merge pull request #924 from vector-im/feature/crossing_fix_2
Feature/crossing fix 2
2020-02-01 17:17:30 +01:00
Valere
26400e6372 Prompt before resetting keys 2020-02-01 15:20:04 +01:00
Valere
282be29247 temp shield for read only 2020-02-01 15:20:04 +01:00
Benoit Marty
6ee7ee1460 Fix blink effect 2020-02-01 15:04:22 +01:00
Valere
2e0a84ccc9 display profile faster from known info 2020-02-01 15:03:00 +01:00
Valere
9d6e7d7bd0 Learn more shows my devices when keys not trusted 2020-02-01 14:36:17 +01:00
Valere
8f7b18239d Warn on verify when private keys not known 2020-02-01 14:36:17 +01:00
Benoit Marty
10094a212c ktlint 2020-02-01 14:02:51 +01:00
Benoit Marty
53e36dca9c ZXing 3.3.3 because of https://github.com/zxing/zxing/issues/1170 2020-02-01 13:52:33 +01:00
Benoit Marty
6ec2dd8c00 Add startWith and logs 2020-02-01 12:12:40 +01:00
Benoit Marty
f098d6cf5b Add startWith 2020-02-01 11:45:45 +01:00
Benoit Marty
cd606ba8a1 RoomMember decoration 2020-02-01 11:37:16 +01:00
Benoit Marty
59abee10f8 Convert to ConstraintLayout 2020-02-01 11:37:16 +01:00
Valere
64df9e23c2 Room Profile / Just show e2e status remove learn more 2020-02-01 11:03:00 +01:00
Valere
fc4f5faffd Update Room decoration algo 2020-02-01 10:21:29 +01:00
Benoit Marty
256f8b77aa Add Timber to rx module 2020-02-01 10:09:22 +01:00
Benoit Marty
f2f775cb99 Add TODOs 2020-02-01 01:17:18 +01:00
Benoit Marty
2616a889ef Merge pull request #923 from vector-im/feature/xcrossing_fix
Decoration in room profile and improve Rx flow
2020-01-31 20:48:20 +01:00
Benoit Marty
ccd4c1ed86 ktlint 2020-01-31 20:46:33 +01:00
Benoit Marty
bb92882958 (partially) Fix glitch 2020-01-31 20:28:57 +01:00
Benoit Marty
46dd17644f Room decoration - in room profile UI 2020-01-31 20:12:02 +01:00
Benoit Marty
7e34b2a672 Room decoration - convert to ConstraintLayout 2020-01-31 20:02:01 +01:00
Benoit Marty
c3c88c387b Improve Rx chain and cleanup 2020-01-31 19:55:22 +01:00
Valere
51e0f945a7 Quick Room Decoration 2020-01-31 18:52:33 +01:00
Benoit Marty
fd3619b100 Merge pull request #770 from vector-im/cross_signing
Cross signing
2020-01-31 17:05:18 +01:00
Benoit Marty
e18b9d5155 Room decoration - UI in room list 2020-01-31 16:10:52 +01:00
Benoit Marty
f5ecf4bd90 Room decoration - UI 2020-01-31 15:02:54 +01:00
Valere
27c74c9118 live device in settings 2020-01-31 14:42:00 +01:00
Benoit Marty
4d91bc934b Fix ktlint 2020-01-31 14:12:03 +01:00
Valere
5c547794f2 Merge branch 'develop' into cross_signing 2020-01-31 14:09:40 +01:00
Benoit Marty
43358cd86c Make self verification work! 2020-01-31 12:18:27 +01:00
Benoit Marty
87b76d10dd Format 2020-01-31 11:11:27 +01:00
Benoit Marty
80f4f95f81 QRCode: requestId is not supposed to be an eventId 2020-01-31 11:11:27 +01:00
Valere
8e5c7239cf Settings Sessions / Now live + support devices with no keys 2020-01-31 10:33:53 +01:00
Valere
7b385c3b36 Quick copy change device -> session 2020-01-31 10:00:44 +01:00
Valere
4fb59aadb1 Fix / ensure RoomKeyRequest are made after device is verified 2020-01-31 09:39:31 +01:00
Valere
850c830e1f Fix / auto ready request when waiting bottomsheet on screen 2020-01-31 09:16:03 +01:00
Benoit Marty
9dde43f65b PR Review: var -> val, internal and other cleanup 2020-01-30 23:40:25 +01:00
Benoit Marty
9d566e9352 Merge pull request #918 from vector-im/feature/sort_room_members
Sort room members by display names
2020-01-30 21:06:40 +01:00
Benoit Marty
eb46a4949f Merge pull request #920 from duncanturk/patch-1
Add "get it on F-Droid"
2020-01-30 21:05:53 +01:00
Benoit Marty
506c2dd262 Merge pull request #921 from vector-im/decorations
Add field in QR code and make some optional
2020-01-30 18:48:30 +01:00
Benoit Marty
ccd857016c ktlint 2020-01-30 18:46:08 +01:00
Benoit Marty
225e4e0433 To Device Verification Request 2020-01-30 18:43:50 +01:00
Benoit Marty
069bd3c258 use isMe 2020-01-30 16:59:22 +01:00
Benoit Marty
fb98d6ef42 QRCode: add other_device_key field and make it optional, along with other_user_key 2020-01-30 16:46:12 +01:00
Benoit Marty
6282f81bc4 Remove typo 2020-01-30 16:46:12 +01:00
Valere
a5ca2b1d34 Fix / incoming start verif popup should not show when bottomsheet there 2020-01-30 16:36:13 +01:00
Valere
50d5ad3625 Self verification + toDevice Request 2020-01-30 16:35:42 +01:00
Valere
03c5e61b2e Fix / post merge 2020-01-30 16:35:06 +01:00
Valere
4ddd831d7f Prepare support for toDevice .request 2020-01-30 16:11:34 +01:00
Valere
ff95392e10 Fix / Refresh trust state on own keys/device trust change 2020-01-30 16:10:59 +01:00
Benoit Marty
e2c2c2418c Merge pull request #919 from vector-im/qr_step_validate
Qr step validate
2020-01-30 14:14:16 +01:00
Christopher Rossbach
e44dc347c6 Add "get it on F-Droid"
RiotX is on F-Droid now
2020-01-30 12:07:33 +01:00
Benoit Marty
fbd0bbc575 Improve clarity of the algorithm to enable encryption for DMs 2020-01-30 11:24:05 +01:00
ganfra
b848d0530f Update realm to 6.1.0: should fix some of the native crashes 2020-01-30 11:04:37 +01:00
Benoit Marty
2bccd19f84 QRcode: Url encode the keys 2020-01-30 10:17:04 +01:00
Benoit Marty
2111daea52 Add a step to confirm that other user has scanned the SR code 2020-01-30 10:09:59 +01:00
Benoit Marty
57a13fa30d Sort room members by display names 2020-01-29 18:07:57 +01:00
Benoit Marty
c4649a5824 Merge pull request #916 from vector-im/debug_qr
Negotiate E2E by default for DMs (#907)
2020-01-29 18:02:43 +01:00
Benoit Marty
6f6c3184dd Avoid test if previous result is null 2020-01-29 17:13:41 +01:00
Benoit Marty
d7feb6dd5c Merge pull request #913 from vector-im/feature/rainbow
Rainbow
2020-01-29 16:35:34 +01:00
Benoit Marty
e6c3f7c77b Nicer API 2020-01-29 16:26:19 +01:00
Benoit Marty
8b6ffc2fb1 ktlint 2020-01-29 16:18:33 +01:00
Benoit Marty
ae36846aaf Negotiate E2E by default for DMs (#907) 2020-01-29 16:11:23 +01:00
Benoit Marty
237da5bb16 No need to have mutable list in param 2020-01-29 16:03:28 +01:00
Benoit Marty
a4abe5f552 Set timeout to 60s when creating a room 2020-01-29 16:00:07 +01:00
Benoit Marty
e1ddde5501 Make CreateRoomParams a regular data class 2020-01-29 14:23:32 +01:00
Valere
754ca3c582 Fix / fail to update usk when dl own keys 2020-01-29 14:00:02 +01:00
Benoit Marty
70b04dbaea Disable not passing test, to avoid waiting too long when running the test suite 2020-01-29 12:36:38 +01:00
Benoit Marty
b44b6726ed Also update the tests 2020-01-29 12:29:19 +01:00
Benoit Marty
80ec199135 Convert VerificationTxState to a sealed class 2020-01-29 12:00:49 +01:00
Benoit Marty
305dfd4c7a Merge pull request #911 from vector-im/feature/encryption_notice
Modify encryption notice
2020-01-29 10:32:23 +01:00
Benoit Marty
73bb4bb785 Merge pull request #912 from vector-im/feature/e2e_opt_out
e2e opt in when creating room (not DM)
2020-01-29 10:29:33 +01:00
Valere
1d84ccd64a Merge pull request #881 from vector-im/xsigning_sdk
Xsigning sdk
2020-01-29 10:26:46 +01:00
Valere
63e36b0403 Remove unused test 2020-01-29 10:16:57 +01:00
Valere
2c568b4de9 clean klint 2020-01-29 09:59:09 +01:00
Benoit Marty
27fe4e3680 Fix build and add tests 2020-01-29 09:44:53 +01:00
Benoit Marty
007b0cabf2 Add a few TUs 2020-01-28 22:43:10 +01:00
Benoit Marty
b2338dfcd3 Make the TU passes 2020-01-28 22:35:40 +01:00
Benoit Marty
6d7d4993a6 Add TUs for RainbowGenerator (not all passing) 2020-01-28 21:56:02 +01:00
Benoit Marty
da9b9f4864 Make the whole cell clickable 2020-01-28 21:31:02 +01:00
Benoit Marty
ef0b438a89 Give the possibility to enable encryption when creating room (#837) 2020-01-28 21:31:02 +01:00
Valere
9a79297e14 Merge pull request #910 from vector-im/qr_code_step_2
Qr code step 2
2020-01-28 18:11:09 +01:00
Valere
a57393cafa More log + quick fix in settings 2020-01-28 18:09:17 +01:00
Benoit Marty
e12de3fba0 Merge pull request #891 from vector-im/feature/event-unknown
Feature/event unknown
2020-01-28 17:30:02 +01:00
Benoit Marty
2eeeea3377 Encryption is enabled only for MEGOLM. 2020-01-28 17:19:22 +01:00
Benoit Marty
976a8fc568 Hide the algorithm when turning on e2e (#897) 2020-01-28 16:36:28 +01:00
Benoit Marty
b7ecfd997d Fix compilation issue after rebase 2020-01-28 16:02:20 +01:00
Benoit Marty
e0b3ea7e48 QrCode: WIP 2020-01-28 15:55:44 +01:00
Benoit Marty
9c829e62e6 QrCode: WIP 2020-01-28 15:55:44 +01:00
Benoit Marty
20c7e4c3ad QrCode: improve UI 2020-01-28 15:55:44 +01:00
Benoit Marty
69ab5e43d5 QrCode: WIP 2020-01-28 15:55:02 +01:00
Benoit Marty
f46023e84c QrCode: WIP 2020-01-28 15:55:02 +01:00
Benoit Marty
d8d465f70b QrCode: WIP 2020-01-28 15:53:57 +01:00
Benoit Marty
8659216955 QrCode: WIP 2020-01-28 15:53:57 +01:00
Benoit Marty
39e746413a QrCode: WIP 2020-01-28 15:53:57 +01:00
Benoit Marty
0aaba26f17 Rename classes 2020-01-28 15:53:57 +01:00
Benoit Marty
fc04833157 Rename parameter 2020-01-28 15:53:57 +01:00
Benoit Marty
f80861bed8 Add TODO 2020-01-28 15:53:57 +01:00
Benoit Marty
9e796067cc Do not support SHOW or SCAN if cross-signing is not enabled 2020-01-28 15:53:57 +01:00
Benoit Marty
fb5148fd43 Avoid to inject credential (again) 2020-01-28 15:52:17 +01:00
Benoit Marty
be77017209 Avoid injecting credentials. Inject userId and deviceId instead
And cleanup API
2020-01-28 15:48:09 +01:00
Benoit Marty
962b85b041 Add TODO 2020-01-28 15:48:09 +01:00
Benoit Marty
adc2d570eb QR code: handle the case where other user can scan QR codes 2020-01-28 15:48:09 +01:00
Benoit Marty
df49ab8362 QR code: update code which build URL 2020-01-28 15:48:09 +01:00
Benoit Marty
efc8cfb9a1 QR code: modify APIs 2020-01-28 15:48:09 +01:00
Benoit Marty
345824daa2 Keep on renaming 2020-01-28 15:48:09 +01:00
Benoit Marty
050eb0af9d Create dedicated View and Epoxy item for QrCode 2020-01-28 15:48:09 +01:00
Valere
ca4ed6e1bd Fix / Error management and clear keys 2020-01-28 15:35:11 +01:00
Valere
f021f8110d post merge 2020-01-28 12:04:19 +01:00
Valere
109ff4f908 Merge branch 'cross_signing' into xsigning_sdk 2020-01-28 11:33:54 +01:00
Valere
c9f0209ebf post merge fix 2020-01-28 11:23:37 +01:00
Valere
7daa088618 Merge branch 'develop' into cross_signing 2020-01-28 11:13:31 +01:00
Valere
83e44ac96e Fix / cross signing info live data not always updated 2020-01-28 11:02:12 +01:00
Benoit Marty
f3e88c75cf Fix typo 2020-01-28 09:42:56 +01:00
Valere
ea6e8a6789 Basic debug screen to setup keys 2020-01-27 23:51:08 +01:00
Valere
6cece03998 Profile detailed device info + verify manually 2020-01-27 17:55:00 +01:00
Valere
08ae0b485a Profile Screen / Add show device list trust screen 2020-01-27 09:25:58 +01:00
Valere
665c577747 SDK / update trust on key change + live method in Service 2020-01-27 09:25:16 +01:00
Valere
d60351bcb7 Verify from RoomMember Profile 2020-01-24 19:15:23 +01:00
Valere
a758efc018 Renamed room transport classes 2020-01-24 11:32:24 +01:00
Valere
d0addc4c4f Refactored Verification Classes 2020-01-24 11:29:26 +01:00
Valere
bb5179140c Update profile screen for xSigning 2020-01-24 09:14:32 +01:00
Benoit Marty
e9ea69f055 Add support for /rainbow and /rainbowme command (#879) 2020-01-23 23:34:21 +01:00
Valere
e47791f290 Merge pull request #888 from vector-im/qr_code
Qr code
2020-01-23 16:41:23 +01:00
Valere
91ae96a153 QuickFix / Do not verify yourself in dm 2020-01-23 16:16:57 +01:00
Valere
0148949a4f Fix / prevent verification toaster to show when in good room 2020-01-23 16:13:46 +01:00
Valere
65cb812fc6 Fix / Unknown transaction when started by other after request 2020-01-23 16:04:29 +01:00
Valere
e8a4f1fb90 Fix: .cancel won't appear in debug show all 2020-01-23 16:04:03 +01:00
Benoit Marty
632832a651 Nearly same code for DefaultItem and NoticeItem 2020-01-23 15:44:41 +01:00
Benoit Marty
d530c64a84 Render defaultItem as other item: display user avatar
Also ensure bottom sheet always has a header, for user avatar and date
2020-01-23 15:35:46 +01:00
Benoit Marty
426e291ce9 i18n for RiotX limitation messages 2020-01-23 14:46:36 +01:00
Valere
1276d1f39d Update My device list + action to verify 2020-01-23 13:57:17 +01:00
Benoit Marty
4a1012cf81 Add TODOs 2020-01-23 11:48:08 +01:00
Benoit Marty
5819790c1b Distinguish Show SR code and Scan QR code capability 2020-01-23 11:25:44 +01:00
Benoit Marty
b3089343ad Support SCAN method (WIP) 2020-01-23 10:47:29 +01:00
Benoit Marty
37b950897f Base64 no wrap and extension for the reverse operation 2020-01-23 10:17:07 +01:00
Benoit Marty
d2fab91e9d Improve code 2020-01-22 19:08:21 +01:00
Benoit Marty
c323b61575 Ignore typo 2020-01-22 18:27:59 +01:00
Benoit Marty
0e55f81879 Ensure all is escaped properly 2020-01-22 18:26:34 +01:00
Benoit Marty
cbf418c401 Update after MSC change 2020-01-22 18:22:01 +01:00
Benoit Marty
41c691f26c Create QrCodeData class and method to convert to URL and vice versa, with TUs 2020-01-22 17:58:25 +01:00
Benoit Marty
81337d1624 Also keep the same parameter order: (userId, deviceId) to avoid silly errors 2020-01-22 17:00:16 +01:00
Benoit Marty
79df6b8402 Start plugin QR code to the code 2020-01-22 15:56:43 +01:00
Benoit Marty
537b1be0c5 Update wording 2020-01-22 15:26:26 +01:00
Benoit Marty
c1259161e5 QRCode: generate and scan QRCodes 2020-01-22 15:03:56 +01:00
Benoit Marty
75e39535bc Merge pull request #883 from vector-im/feature/share
Improve the room list when sharing to RiotX
2020-01-22 11:49:50 +01:00
Benoit Marty
c971f18fc0 Add section header when displaying room list to share (#771) 2020-01-22 11:49:30 +01:00
Benoit Marty
3c2fa40b58 Sharing things to RiotX: sort list by recent room first (#771) 2020-01-22 11:49:04 +01:00
Benoit Marty
0b74863c6d Merge pull request #877 from vector-im/feature/version_name
F-Droid: fix the "-dev" issue in version name (#815)
2020-01-22 11:45:36 +01:00
Benoit Marty
dca950140d Merge branch 'develop' into feature/version_name 2020-01-22 11:45:27 +01:00
Benoit Marty
a13a78ccc0 Merge pull request #874 from vector-im/feature/authors
Builds repoductibility and Authors file
2020-01-22 11:43:51 +01:00
Benoit Marty
7327dc97b4 Merge pull request #876 from vector-im/feature/room_settings
Room settings, and enable encryption in unencrypted rooms (#212)
2020-01-22 11:40:26 +01:00
Benoit Marty
da3e547d82 Create facility extension to observe ViewEvents 2020-01-21 15:08:49 +01:00
Valere
a0aa1f34d3 Quick Fix todevice verif broken
Added dbg screen for cross signing
2020-01-21 14:58:06 +01:00
Benoit Marty
e81c804ed6 Ensure when will be exhaustive 2020-01-21 12:51:28 +01:00
Benoit Marty
be371f9279 Introduce ViewEvents in ViewModel and code harmonization 2020-01-21 12:51:28 +01:00
Valere
a6364f0be5 remove dead code 2020-01-21 10:25:57 +01:00
Valere
390879e3fd Added check self keys + force DL after initialize Xsigning 2020-01-21 10:25:57 +01:00
Valere
6ab540045b Refactoring / deprecation of MXDeviceInfo
introduced TrustLevels
2020-01-21 10:25:57 +01:00
Valere
98ba2d39a8 SAS verif, support signing and verification of Cross Signing 2020-01-21 10:25:57 +01:00
Valere
859c75df98 Initial commit 2020-01-21 10:25:57 +01:00
Valere
ea9166e0c6 Merge pull request #811 from vector-im/verification_toasters
[Cross Signing DM Verif]  Basic Incoming request toast + cleaning
2020-01-21 10:21:24 +01:00
Benoit Marty
d672313649 Room settings: use boolean instead of Async 2020-01-20 17:41:29 +01:00
Benoit Marty
f5e6b4b857 F-Droid: fix the "-dev" issue in version name (#815) 2020-01-20 16:08:57 +01:00
Benoit Marty
28db05e509 Cleanup and copy wording from Riot-Web 2020-01-20 15:39:14 +01:00
Benoit Marty
56b140fcb4 Room settings: rename stuff for genericity 2020-01-20 15:23:40 +01:00
Benoit Marty
eee6969b02 Room settings, and enable encryption in unencrypted rooms (#212) 2020-01-20 15:11:21 +01:00
Benoit Marty
825427b7b0 Add a first content into the AUTHORS file 2020-01-17 16:50:18 +01:00
Benoit Marty
8c32796d5c Ensure builds are reproducible (#842) 2020-01-17 16:06:58 +01:00
Valere
e45c1e6c2a Fix / post merge issues 2020-01-17 15:17:16 +01:00
Benoit Marty
188cd6beff Verification: improve blinking effect (not perfect yet) 2020-01-17 15:05:06 +01:00
Benoit Marty
aaeb54db7c Verification: Introduce VerificationMethod enum 2020-01-17 15:05:06 +01:00
Benoit Marty
92f26bc20a Verification: migrate to Epoxy - Cleanup 2020-01-17 15:05:06 +01:00
Benoit Marty
fc22b7988f Verification: migrate to Epoxy - Add missing icons 2020-01-17 15:05:06 +01:00
Benoit Marty
878bae1c45 Verification: migrate to Epoxy - Choose Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
a8e81d95cf Verification: migrate to Epoxy - Disable item animation 2020-01-17 15:05:06 +01:00
Benoit Marty
cd1665a8e8 Verification: migrate to Epoxy - Conclusion 2020-01-17 15:05:06 +01:00
Benoit Marty
7170471686 Verification: migrate to Epoxy - create sub packages 2020-01-17 15:05:06 +01:00
Benoit Marty
98020404ff Verification: migrate to Epoxy - rename package 2020-01-17 15:05:06 +01:00
Benoit Marty
b2348427bd Verification: migrate to Epoxy - Emoji Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
832df59b58 Verification: migrate to Epoxy - Request Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
32689facc5 Verification: migrate to Epoxy - Request Fragment 2020-01-17 15:05:06 +01:00
Benoit Marty
d3071e5816 Rename layout for consistency 2020-01-17 15:04:36 +01:00
Benoit Marty
83a6f564c3 Ensure BottomSheets call ButterKnife unbinder 2020-01-17 15:04:36 +01:00
Benoit Marty
3b420dbb50 typo 2020-01-17 15:04:36 +01:00
Benoit Marty
9fe155bafd postWork now returns the Cancellable 2020-01-17 15:04:36 +01:00
Benoit Marty
3c982866d8 Restore lost Fragment binding... 2020-01-17 15:04:36 +01:00
Benoit Marty
494ad83704 Inject WorkManagerProvider, to avoid injecting the Android context
Also ensure WorkManager uses a distinct tags for each session (for future multi-sessions support)
2020-01-17 15:04:36 +01:00
Benoit Marty
4543658ae0 Extends SessionWorkerParams 2020-01-17 15:04:36 +01:00
Benoit Marty
689fd1ea90 Fix issue with SessionId for the worker
Also rename some variables
2020-01-17 15:04:36 +01:00
Valere
a95410c118 fix rebase 2020-01-17 15:04:36 +01:00
Valere
8749e49e80 Basic Incoming request toast + cleaning 2020-01-17 15:04:36 +01:00
Valere
8400ab6efe Merge branch 'develop' into cross_signing 2020-01-17 14:57:08 +01:00
Benoit Marty
d1699279fe Version++ 2020-01-17 14:25:25 +01:00
Benoit Marty
f471d9cff8 Merge branch 'release/0.13.0' 2020-01-17 14:24:11 +01:00
Benoit Marty
115058124c Merge branch 'release/0.13.0' into develop 2020-01-17 14:24:10 +01:00
Benoit Marty
7604748d3f Prepare release 0.13.0 2020-01-17 14:23:58 +01:00
Benoit Marty
d46a3ab87f Update CHANGES.md 2020-01-17 14:10:47 +01:00
Benoit Marty
b375463aec Merge pull request #854 from vector-im/feature/profiles_improvements
Feature/profiles improvements
2020-01-17 12:22:16 +01:00
Benoit Marty
cfb752c220 Merge pull request #851 from vector-im/feature/code_style
Share the same code formatting rules
2020-01-17 12:21:34 +01:00
Benoit Marty
c59712732a Introduce listener type for Epoxy items 2020-01-17 12:14:24 +01:00
Benoit Marty
626ebd9c63 Merge pull request #850 from vector-im/feature/e2e_attachments_cleanup
Feature/e2e attachments cleanup
2020-01-17 12:12:48 +01:00
Benoit Marty
5148d7ab46 Room profile: open it also when clicking on the room avatar 2020-01-17 10:15:04 +01:00
Benoit Marty
8c2a55a5a2 Settings: restore "Show read receipts" setting and create categories of per Riot-Web 2020-01-17 10:10:46 +01:00
Benoit Marty
34b6dd4270 Room profile: open room member profile from Event bottom sheet 2020-01-16 21:48:10 +01:00
Benoit Marty
79a68a36bd Room profile: ensure RoomMemberSummaries order is guaranteed 2020-01-16 21:29:05 +01:00
Benoit Marty
78408fa0ec Room profile: create extension join for Collections 2020-01-16 21:18:14 +01:00
Benoit Marty
b56ee9a377 Room profile: fix issue with divider color 2020-01-16 20:53:38 +01:00
Benoit Marty
9eff459ed6 Room profile: remove an extra divider 2020-01-16 16:43:37 +01:00
Benoit Marty
cd44b60bd5 Room profile: make link in room topic clickable 2020-01-16 16:36:56 +01:00
Benoit Marty
17ed8da57a Room profile: fix issue with icon 2020-01-16 16:27:21 +01:00
Benoit Marty
8ae51a95c5 Add formatting policy in the Contributing guide 2020-01-16 16:02:41 +01:00
Benoit Marty
f80f22fed2 XML formatting rule 2020-01-16 15:58:52 +01:00
Benoit Marty
fa57d0f6d5 160 char max per line 2020-01-16 15:27:00 +01:00
Benoit Marty
1104b5d483 DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION 2020-01-16 14:26:52 +01:00
Benoit Marty
464462022c Kotlin code convention 2020-01-16 14:17:29 +01:00
Benoit Marty
1c63d3f8f0 Add rule 2020-01-16 14:15:50 +01:00
Benoit Marty
a916bd4ba8 Share the same code formatting rules 2020-01-16 14:05:14 +01:00
Benoit Marty
15e2c9f3a3 Fix formatting 2020-01-16 12:55:55 +01:00
Benoit Marty
f1a2fb51f5 Properly configure Snackbar styles 2020-01-16 12:51:33 +01:00
Benoit Marty
b0aa9fbd8f Inform user when the download of a file starts 2020-01-16 12:20:39 +01:00
Benoit Marty
d72f1ac576 Avoid exposing internal classes 2020-01-16 11:36:53 +01:00
Benoit Marty
ca157c7567 Better logs 2020-01-16 10:57:08 +01:00
Benoit Marty
ae26bf3369 Signout also clear cache 2020-01-16 10:57:08 +01:00
Benoit Marty
159c96681f Improve attachment encryption and decryption code 2020-01-16 10:57:08 +01:00
Benoit Marty
96d6b75037 Fix broken link 2020-01-16 10:57:08 +01:00
Benoit Marty
72e6181f00 Merge pull request #839 from vector-im/feature/developer
Show all hidden event in the timeline when the developer settings is ON
2020-01-16 10:56:27 +01:00
Benoit Marty
4ae09b8716 Merge pull request #831 from david1hyman/file-decryption-fix
File decryption path was writing incorrect stream to file
2020-01-16 10:50:15 +01:00
ganfra
f128ed437f Merge pull request #843 from vector-im/feature/room_profile
Feature/room profile
2020-01-15 19:17:50 +01:00
ganfra
11c8c8c2bd Fix fdroid background sync 2020-01-15 19:16:50 +01:00
ganfra
2c331671ee Fix string 2020-01-15 17:44:38 +01:00
ganfra
b44ddcfd61 Clean code after Benoit's review #2 2020-01-15 14:57:21 +01:00
ganfra
7c0f2d6e32 Test: fix import 2020-01-15 11:48:02 +01:00
ganfra
52de14b1b5 Clean code after Benoit's review 2020-01-15 11:46:33 +01:00
ganfra
d6e6092eea Profile: add PowerLevelContent for building future actions + fix divider 2020-01-14 19:14:15 +01:00
ganfra
9671a77e5d Clean code and update CHANGES 2020-01-14 18:48:13 +01:00
ganfra
d3415d345f Merge branch 'develop' into feature/room_profile 2020-01-14 18:35:01 +01:00
ganfra
df4df81ef3 Profile: handle ignore/unignore action + adjust UI 2020-01-14 17:08:21 +01:00
Valere
a7c948815c Merge branch 'develop' into cross_signing 2020-01-14 12:31:29 +01:00
Benoit Marty
9d5197b1c8 Show all hidden event in the timeline when the developer settings is ON 2020-01-13 22:47:41 +01:00
ganfra
162f0949fa Profile: Start fetching profile info from a user 2020-01-13 18:44:01 +01:00
ganfra
ae1a24e948 Room member profile: branch the UI and fix some UI issues 2020-01-13 16:49:14 +01:00
Benoit Marty
b5fead18fe Merge pull request #833 from vector-im/feature/typing
Send and render typing events (#564)
2020-01-13 15:17:43 +01:00
Benoit Marty
3c682430d9 Merge pull request #829 from vector-im/feature/stab
Feature/stab
2020-01-13 15:13:07 +01:00
Benoit Marty
717965bc37 Update comment 2020-01-13 15:12:39 +01:00
Benoit Marty
ecc463e920 Cleanup and fix a bug 2020-01-13 10:13:12 +01:00
Benoit Marty
f3e52b96c0 Cleanup 2020-01-11 22:12:56 +01:00
Benoit Marty
dd81fce8d8 Send and render typing events (#564) 2020-01-11 20:50:09 +01:00
Valere
7354eab061 Post merge fixes 2020-01-11 10:16:09 +01:00
David Hyman
1ae58aa6ad DefaultFileService - code was passing the inputStream to the decryption method
but not storing the output of the method anywhere
then it was writing inputStream to file and returning that file handle
changed inputStream to var and used it to store output of decryption method
2020-01-10 14:53:40 -05:00
Valere
fb9abefe59 Merge branch 'develop' into cross_signing 2020-01-10 18:38:54 +01:00
Benoit Marty
095216349e Exception -> Throwable 2020-01-10 18:35:29 +01:00
Benoit Marty
550908fa70 Render events m.room.encryption and m.room.guest_access in the timeline 2020-01-10 18:30:10 +01:00
Benoit Marty
0dbca829ea Fix crash with RoomCreationParams 2020-01-10 18:07:14 +01:00
Benoit Marty
03b5b098c7 Change the way versionCode is computed (#827) 2020-01-10 17:29:34 +01:00
ganfra
171ec4fbdc Room member list: group by power level 2020-01-10 17:03:11 +01:00
Benoit Marty
32d2daee3c Exclude current user from autocompletion with room members 2020-01-10 16:28:14 +01:00
Benoit Marty
de84bb7535 Use more developerMode() 2020-01-10 11:03:42 +01:00
ganfra
81712ae736 RoomProfile: fix some rendering + change anim 2020-01-09 19:47:54 +01:00
ganfra
289951ea4a RoomMemberList : start showing items 2020-01-09 15:54:36 +01:00
Benoit Marty
8c9c65837d Version++ 2020-01-09 15:32:37 +01:00
Benoit Marty
24a7ce7d98 Merge branch 'release/0.12.0' 2020-01-09 15:31:30 +01:00
Benoit Marty
d486e613d8 Merge branch 'release/0.12.0' into develop 2020-01-09 15:31:29 +01:00
Benoit Marty
598d67234c Prepare release 0.12.0 2020-01-09 15:31:13 +01:00
Benoit Marty
fea4f2e129 Merge pull request #816 from vector-im/feature/integration_tests
Start porting of integration tests - Fix issue with multiple sessions
2020-01-09 15:28:29 +01:00
Benoit Marty
687ea1b5b3 ktlint 2020-01-09 15:28:16 +01:00
Benoit Marty
47e3b8ec46 Ensure foreground notification is always displayed 2020-01-09 15:09:37 +01:00
Benoit Marty
dd8c908dc7 Code cleanup 2020-01-09 15:02:39 +01:00
Benoit Marty
9775e8c32b Fix crash in syncService 2020-01-09 15:01:16 +01:00
Benoit Marty
e3205fb493 Fix compilation issue after rebase 2020-01-09 14:26:36 +01:00
Benoit Marty
35f011ba37 Fix ktlint issues 2020-01-09 14:20:17 +01:00
Benoit Marty
ed773dbb96 TI: Introduce doSync method 2020-01-09 14:20:17 +01:00
Benoit Marty
fa821826d2 TI: Import ExportEncryption test (passing) 2020-01-09 14:20:17 +01:00
Benoit Marty
293e3e3ce6 TI: Import AttachmentEncryption test (passing) 2020-01-09 14:20:17 +01:00
Benoit Marty
4244c0e48d TI: Import SAS Test - WIP 2020-01-09 14:20:17 +01:00
Benoit Marty
76e45431da TI: Import keys backup tests 2020-01-09 14:20:17 +01:00
Benoit Marty
f3fb07079e Cleanup tests 2020-01-09 14:20:17 +01:00
Benoit Marty
3ceac70536 Enable encryption on a room, SDK part (#212) 2020-01-09 14:20:17 +01:00
Benoit Marty
0f7209df1f TI: finish the work to identify a session with a sessionId 2020-01-09 14:20:17 +01:00
Benoit Marty
e177251ec0 TI: inject EventBus to allow multiple sessions - WIP 2020-01-09 14:20:17 +01:00
Benoit Marty
6746f68411 TI: create account 2020-01-09 14:20:17 +01:00
Benoit Marty
fc6d845c0d Import tests from legacy SDK 2020-01-09 14:20:17 +01:00
Benoit Marty
93cdce6c3e Cleanup tests 2020-01-09 14:20:17 +01:00
Benoit Marty
ae3381227c Add Unit tests from legacy SDK 2020-01-09 14:20:17 +01:00
Benoit Marty
b6a1ff1ca4 Import string from Riot legacy 2020-01-09 14:17:17 +01:00
Benoit Marty
7ec0227528 Merge pull request #824 from vector-im/feature/wording
Email domain can be limited on some homeserver, i18n of the displayed error (#754)
2020-01-09 12:26:28 +01:00
ganfra
15639b45cf Introduce RoomMemberProfile files 2020-01-09 12:10:49 +01:00
ganfra
f18ec8d021 Merge branch 'develop' into feature/room_profile 2020-01-09 11:56:09 +01:00
Benoit Marty
898bf234da Merge pull request #792 from vector-im/feature/stabilization
Feature/stabilization
2020-01-09 11:43:52 +01:00
Benoit Marty
3d0d95c371 Email domain can be limited on some homeserver, i18n of the displayed error (#754) 2020-01-09 11:34:57 +01:00
ganfra
bd4a595f96 ChunkEntityTest: make it compile again 2020-01-09 11:19:08 +01:00
Benoit Marty
0f7d59a8c7 Cleanup during PR review 2020-01-09 09:42:34 +01:00
Benoit Marty
e14b9b3b20 Fix test compilation issue 2020-01-09 08:03:14 +01:00
Benoit Marty
43c4e20819 Merge pull request #820 from vector-im/feature/developer_mode_crash
Developer mode: Fail-fast (#745)
2020-01-09 07:56:06 +01:00
Benoit Marty
5312b44359 Merge pull request #821 from vector-im/feature/query_latch
Remove CountDownLatch (inspired from #419)
2020-01-09 07:55:38 +01:00
ganfra
8c4d8763a2 Merge branch 'develop' into feature/stabilization 2020-01-08 22:28:08 +01:00
ganfra
383605274c Introduce a very simple query langage and refact autocomplete 2020-01-08 22:17:32 +01:00
Benoit Marty
8032490606 Remove CountDownLatch (inspired from #419) 2020-01-08 18:58:51 +01:00
Benoit Marty
a458997ce0 Merge pull request #818 from vector-im/feature/oss
Exclude play-services-oss-licenses library from F-Droid build (#814)
2020-01-08 18:28:31 +01:00
Benoit Marty
29f152f349 Fix CI 2020-01-08 18:21:01 +01:00
Benoit Marty
6ca3ba6c9b Merge pull request #806 from Bubu/fix_697
call /join/{roomIdOrAlias} instead of /rooms/{roomId}/join
2020-01-08 18:17:34 +01:00
Benoit Marty
f4492e570d Merge branch 'develop' into fix_697 2020-01-08 18:17:23 +01:00
Benoit Marty
587fefedb5 Merge pull request #809 from Bubu/fix_807
set homeserver field when populating room directory list
2020-01-08 18:12:42 +01:00
Benoit Marty
943be39e1a Merge branch 'develop' into fix_807 2020-01-08 18:12:22 +01:00
Benoit Marty
616f3d3345 Merge pull request #817 from vector-im/feature/fab_scroll
Show skip to bottom FAB while scrolling down (#752)
2020-01-08 18:07:10 +01:00
Benoit Marty
2b8ecae8e3 Fix CI 2020-01-08 18:05:26 +01:00
Benoit Marty
17c4013383 Developer mode: Fail-fast (#745) 2020-01-08 17:58:26 +01:00
Benoit Marty
d662b4a9b4 Exclude play-services-oss-licenses library from F-Droid build (#814) 2020-01-08 15:57:35 +01:00
Benoit Marty
501ac36040 Reduce size of RoomDetailFragment 2020-01-08 15:05:20 +01:00
Benoit Marty
7575cb286e Show skip to bottom FAB while scrolling down (#752) 2020-01-08 15:05:20 +01:00
ganfra
c60b4ddb5a Timeline: don't wait for realm notification to come back, use it right away to init 2020-01-08 13:59:43 +01:00
ganfra
9970d7ffa0 SDK: get some better queries 2020-01-08 11:55:22 +01:00
ganfra
2dd2a8db6c Emoji data source as singleton 2020-01-08 11:54:42 +01:00
Benoit Marty
8ef5c60e2e RageShake is enabled by default 2020-01-08 11:43:21 +01:00
ganfra
03c3c9ae57 Timeline: clear unlinked should use new parameters 2020-01-07 18:15:48 +01:00
ganfra
38c198fe02 Rx: fetch first before returning live data results 2020-01-07 18:15:07 +01:00
ganfra
42c7421b05 Merge branch 'develop' into feature/stabilization 2020-01-07 14:42:38 +01:00
ganfra
19fb3ce032 Merge branch 'develop' into feature/stabilization 2020-01-07 14:28:23 +01:00
Benoit Marty
5a7f4bed43 ktlint 2020-01-07 14:24:26 +01:00
Benoit Marty
03734a7ad5 Merge pull request #802 from vector-im/feature/sessionId
Identify a session with the userId and the deviceId
2020-01-07 14:23:09 +01:00
ganfra
d710106bbb Clean code 2020-01-07 14:09:04 +01:00
ganfra
f09bf61750 Room detail: try to get some better perfs with fetching data. LiveData is slow as we only use one HandlerThread at the time. Might want Realm 7.0 and frozen objects to rework that 2020-01-07 13:31:34 +01:00
ganfra
f9487f8995 Work on timeline 2020-01-06 18:44:04 +01:00
ganfra
99c523b710 Update libs 2020-01-06 18:43:34 +01:00
ganfra
3cc15387ae Realm: compatch on launch 2020-01-06 18:41:09 +01:00
Benoit Marty
6245763577 Merge pull request #789 from vector-im/feature/christmas_fix
Auto completion for emojis
2020-01-06 14:20:44 +01:00
Benoit Marty
448552d287 Move list of Quick Emoji to Emoji Data Source 2020-01-06 13:48:34 +01:00
Benoit Marty
9ecceafb96 Move comment 2020-01-06 13:47:06 +01:00
Benoit Marty
92d7ebe94f Update changes 2020-01-06 13:47:06 +01:00
Benoit Marty
0e5fcd071c Completion on emoji: display the first 50 results 2020-01-06 13:46:37 +01:00
Benoit Marty
c8e67f8ab4 Completion on emoji WIP 2020-01-06 13:46:10 +01:00
Benoit Marty
5fa2acf60b Completion on emoji 2020-01-06 13:46:10 +01:00
Benoit Marty
9e73e95f55 Ensure there is never twice the same emoji 2020-01-06 13:46:10 +01:00
Benoit Marty
8b4c51139d Completion on emoji WIP 2020-01-06 13:46:10 +01:00
Benoit Marty
8597c2b9a2 Improve API 2020-01-06 13:46:10 +01:00
Benoit Marty
d88e5d8af8 DRY 2020-01-06 13:46:10 +01:00
Benoit Marty
c4fe0bdb7f Split into small methods 2020-01-06 13:46:10 +01:00
Benoit Marty
d73a1135ae Extract AutoComplete feature from RoomDetailFragment 2020-01-06 13:46:10 +01:00
Benoit Marty
ed097bcf37 Merge pull request #798 from vector-im/feature/settings_cleanup
Feature/settings cleanup
2020-01-06 13:41:50 +01:00
Benoit Marty
01db856a5d Improve (a bit) the devices list UX/UI 2020-01-06 10:51:30 +01:00
Benoit Marty
a00f51a264 Settings: rename "developer mode" to "advanced settings" 2020-01-06 10:32:36 +01:00
Valere
06b41af467 Merge pull request #793 from vector-im/outgoing_dm_verif
BottomSheet UX for verification
2020-01-06 10:22:27 +01:00
Marcus Hoffmann
9e8217082c set homeserver field when populating room directory list
fixes #807

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

Fixes #697

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

View File

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

3
.gitignore vendored
View File

@@ -3,7 +3,6 @@
/local.properties
# idea files: exclude everything except dictionnaries
.idea/caches
.idea/codeStyles
.idea/libraries
.idea/*.xml
.DS_Store
@@ -16,3 +15,5 @@
ktlint
.idea/copyright/New_vector.xml
.idea/copyright/profiles_settings.xml
.idea/copyright/New_Vector_Ltd.xml

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

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

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

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

View File

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

View File

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

View File

@@ -1,3 +1,91 @@
Changes in RiotX 0.14.0 (2020-02-01)
===================================================
Features ✨:
- First implementation of Cross-signing
- Enable encryption in unencrypted rooms, from the room settings (#212)
- Negotiate E2E by default for DMs (#907)
Improvements 🙌:
- Sharing things to RiotX: sort list by recent room first (#771)
- Hide the algorithm when turning on e2e (#897)
- Sort room members by display names
Other changes:
- Add support for /rainbow and /rainbowme commands (#879)
Build 🧱:
- Ensure builds are reproducible (#842)
- F-Droid: fix the "-dev" issue in version name (#815)
Changes in RiotX 0.13.0 (2020-01-17)
===================================================
Features ✨:
- Send and render typing events (#564)
- Create Room Profile screen (#54)
- Create Room Member Profile screen (#59)
Improvements 🙌:
- Render events m.room.encryption and m.room.guest_access in the timeline
Bugfix 🐛:
- Fix broken background sync in F-Droid version
- Fix issue with downloaded file on encrypted rooms. The file was not properly decrypted
Build 🧱:
- Change the way versionCode is computed (#827)
Changes in RiotX 0.12.0 (2020-01-09)
===================================================
Improvements 🙌:
- The initial sync is now handled by a foreground service
- Render aliases and canonical alias change in the timeline
- Introduce developer mode in the settings (#745, #796)
- Improve devices list screen
- Add settings for rageshake sensibility
- Fix autocompletion issues and add support for rooms, groups, and emoji (#780)
- Show skip to bottom FAB while scrolling down (#752)
- Enable encryption on a room, SDK part (#212)
Other changes:
- Change the way RiotX identifies a session to allow the SDK to support several sessions with the same user (#800)
- Exclude play-services-oss-licenses library from F-Droid build (#814)
- Email domain can be limited on some homeservers, i18n of the displayed error (#754)
Bugfix 🐛:
- Fix crash when opening room creation screen from the room filtering screen
- Fix avatar image disappearing (#777)
- Fix read marker banner when permalink
- Fix joining upgraded rooms (#697)
- Fix matrix.org room directory not being browsable (#807)
- Hide non working settings (#751)
Changes in RiotX 0.11.0 (2019-12-19)
===================================================
Features ✨:
- Implement soft logout (#281)
Improvements 🙌:
- Handle navigation to room via room alias (#201)
- Open matrix.to link in RiotX (#57)
- Limit sticker size in the timeline
Other changes:
- Use same default room colors than Riot-Web
Bugfix 🐛:
- Scroll breadcrumbs to top when opened
- Render default room name when it starts with an emoji (#477)
- Do not display " (IRC)" in display names https://github.com/vector-im/riot-android/issues/444
- Fix rendering issue with HTML formatted body
- Disable click on Stickers (#703)
Build 🧱:
- Include diff-match-patch sources as dependency
Changes in RiotX 0.10.0 (2019-12-10)
===================================================
@@ -27,6 +115,7 @@ Changes in RiotX 0.9.0 (2019-12-05)
Features ✨:
- Account creation. It's now possible to create account on any homeserver with RiotX (#34)
- Iteration of the login flow (#613)
- [SDK] MSC2241 / verification in DMs (#707)
Improvements 🙌:
- Send mention Pills from composer
@@ -232,7 +321,7 @@ Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-a
=======================================================
Changes in RiotX 0.0.0 (2019-XX-XX)
Changes in RiotX 0.0.0 (2020-XX-XX)
===================================================
Features ✨:

View File

@@ -1,6 +1,6 @@
# Contributing code to Matrix
Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst
Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md
Android support can be found in this [![Riot Android Matrix room #riot-android:matrix.org](https://img.shields.io/matrix/riot-android:matrix.org.svg?label=%23riot-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#riot-android:matrix.org) room.
@@ -11,6 +11,7 @@ Dedicated room for RiotX: [![RiotX Android Matrix room #riot-android:matrix.org]
## Android Studio settings
Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`).
Please ensure that your using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them.
## Compilation

View File

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

View File

@@ -10,7 +10,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.3.2'
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
@@ -45,12 +45,6 @@ allprojects {
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
google()
jcenter()
maven {
url 'https://repo.adobe.com/nexus/content/repositories/public/'
content {
includeGroupByRegex "diff_match_patch"
}
}
}
tasks.withType(JavaCompile).all {

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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.CompletableEmitter
internal class MatrixCallbackCompletable<T>(private val completableEmitter: CompletableEmitter) : MatrixCallback<T> {
override fun onSuccess(data: T) {
completableEmitter.onComplete()
}
override fun onFailure(failure: Throwable) {
completableEmitter.tryOnError(failure)
}
}
fun Cancelable.toCompletable(completableEmitter: CompletableEmitter) {
completableEmitter.setCancellable {
this.cancel()
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.SingleEmitter
internal class MatrixCallbackSingle<T>(private val singleEmitter: SingleEmitter<T>) : MatrixCallback<T> {
override fun onSuccess(data: T) {
singleEmitter.onSuccess(data)
}
override fun onFailure(failure: Throwable) {
singleEmitter.tryOnError(failure)
}
}
fun <T> Cancelable.toSingle(singleEmitter: SingleEmitter<T>) {
singleEmitter.setCancellable {
this.cancel()
}
}

View File

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

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.rx
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import io.reactivex.Completable
import io.reactivex.Single
fun <T> singleBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Single<T> = Single.create {
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
override fun onSuccess(data: T) {
it.onSuccess(data)
}
override fun onFailure(failure: Throwable) {
it.tryOnError(failure)
}
}
val cancelable = builder(callback)
it.setCancellable {
cancelable.cancel()
}
}
fun <T> completableBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Completable = Completable.create {
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
override fun onSuccess(data: T) {
it.onComplete()
}
override fun onFailure(failure: Throwable) {
it.tryOnError(failure)
}
}
val cancelable = builder(callback)
it.setCancellable {
cancelable.cancel()
}
}

View File

@@ -16,33 +16,128 @@
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
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.api.session.room.send.UserDraft
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional
import 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) {
class RxRoom(private val room: Room, private val session: Session) {
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
return room.getRoomSummaryLive().asObservable()
val summaryObservable = room.getRoomSummaryLive()
.asObservable()
.startWith(room.roomSummary().toOptional())
.doOnNext { Timber.d("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.d("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.d("RX: CryptoDeviceInfo emitted. Size: ${it.size}") }
}
.doOnNext { Timber.d("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.d("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.d("RX: final room summary emitted for ${it.getOrNull()?.roomId}") }
}
fun liveRoomMemberIds(): Observable<List<String>> {
return room.getRoomMemberIdsLive().asObservable()
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable<List<RoomMemberSummary>> {
val roomMembersObservable = room.getRoomMembersLive(queryParams).asObservable()
.startWith(room.getRoomMembers(queryParams))
.doOnNext { Timber.d("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.d("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.d("RX: final room members emitted. Size: ${it.size}") }
}
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
return room.getEventSummaryLive(eventId).asObservable()
return room.getEventAnnotationsSummaryLive(eventId).asObservable()
.startWith(room.getEventAnnotationsSummary(eventId).toOptional())
}
fun liveTimelineEvent(eventId: String): Observable<Optional<TimelineEvent>> {
return room.getTimeLineEventLive(eventId).asObservable()
.startWith(room.getTimeLineEvent(eventId).toOptional())
}
fun liveStateEvent(eventType: String): Observable<Optional<Event>> {
return room.getStateEventLive(eventType).asObservable()
.startWith(room.getStateEvent(eventType).toOptional())
}
fun liveReadMarker(): Observable<Optional<String>> {
@@ -53,13 +148,13 @@ class RxRoom(private val room: Room) {
return room.getMyReadReceiptLive().asObservable()
}
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
fun loadRoomMembersIfNeeded(): Single<Unit> = singleBuilder {
room.loadRoomMembersIfNeeded(it)
}
fun joinRoom(reason: String? = null,
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
room.join(reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
room.join(reason, viaServers, it)
}
fun liveEventReadReceipts(eventId: String): Observable<List<ReadReceipt>> {
@@ -75,6 +170,6 @@ class RxRoom(private val room: Room) {
}
}
fun Room.rx(): RxRoom {
return RxRoom(this)
fun Room.rx(session: Session): RxRoom {
return RxRoom(this, session)
}

View File

@@ -18,68 +18,122 @@ package im.vector.matrix.rx
import androidx.paging.PagedList
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
import im.vector.matrix.android.api.session.group.GroupSummaryQueryParams
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.api.util.toOptional
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.functions.BiFunction
import timber.log.Timber
class RxSession(private val session: Session) {
fun liveRoomSummaries(): Observable<List<RoomSummary>> {
return session.liveRoomSummaries().asObservable()
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams): Observable<List<RoomSummary>> {
val summariesObservable = session.getRoomSummariesLive(queryParams).asObservable()
.startWith(session.getRoomSummaries(queryParams))
.doOnNext { Timber.d("RX: summaries emitted: size: ${it.size}") }
val cryptoDeviceInfoObservable = session.getLiveCryptoDeviceInfo().asObservable()
.startWith(emptyList<CryptoDeviceInfo>())
.doOnNext { Timber.d("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}") }
}
fun liveGroupSummaries(): Observable<List<GroupSummary>> {
return session.liveGroupSummaries().asObservable()
fun liveGroupSummaries(queryParams: GroupSummaryQueryParams): Observable<List<GroupSummary>> {
return session.getGroupSummariesLive(queryParams).asObservable()
.startWith(session.getGroupSummaries(queryParams))
}
fun liveBreadcrumbs(): Observable<List<RoomSummary>> {
return session.liveBreadcrumbs().asObservable()
return session.getBreadcrumbsLive().asObservable()
.startWith(session.getBreadcrumbs())
}
fun liveSyncState(): Observable<SyncState> {
return session.syncState().asObservable()
return session.getSyncStateLive().asObservable()
}
fun livePushers(): Observable<List<Pusher>> {
return session.livePushers().asObservable()
return session.getPushersLive().asObservable()
}
fun liveUser(userId: String): Observable<Optional<User>> {
return session.liveUser(userId).asObservable().distinctUntilChanged()
return session.getUserLive(userId).asObservable()
.startWith(session.getUser(userId).toOptional())
}
fun liveUsers(): Observable<List<User>> {
return session.liveUsers().asObservable()
return session.getUsersLive().asObservable()
}
fun liveIgnoredUsers(): Observable<List<User>> {
return session.liveIgnoredUsers().asObservable()
return session.getIgnoredUsersLive().asObservable()
}
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
return session.livePagedUsers(filter).asObservable()
return session.getPagedUsersLive(filter).asObservable()
}
fun createRoom(roomParams: CreateRoomParams): Single<String> = Single.create {
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
session.createRoom(roomParams, it)
}
fun searchUsersDirectory(search: String,
limit: Int,
excludedUserIds: Set<String>): Single<List<User>> = Single.create {
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
excludedUserIds: Set<String>): Single<List<User>> = singleBuilder {
session.searchUsersDirectory(search, limit, excludedUserIds, it)
}
fun joinRoom(roomId: String,
reason: String? = null,
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
session.joinRoom(roomId, reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
session.joinRoom(roomId, reason, viaServers, it)
}
fun getRoomIdByAlias(roomAlias: String,
searchOnServer: Boolean): Single<Optional<String>> = singleBuilder {
session.getRoomIdByAlias(roomAlias, searchOnServer, it)
}
fun getProfileInfo(userId: String): Single<JsonDict> = singleBuilder {
session.getProfile(userId, it)
}
fun liveUserCryptoDevices(userId: String): Observable<List<CryptoDeviceInfo>> {
return session.getLiveCryptoDeviceInfo(userId).asObservable()
}
fun liveCrossSigningInfo(userId: String): Observable<Optional<MXCrossSigningInfo>> {
return session.getCrossSigningService().getLiveCrossSigningKeys(userId).asObservable()
.startWith(session.getCrossSigningService().getUserCrossSigningKeys(userId).toOptional())
}
}

View File

@@ -10,7 +10,7 @@ buildscript {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:5.12.0"
classpath "io.realm:realm-gradle-plugin:6.1.0"
}
}
@@ -74,7 +74,7 @@ android {
}
static def gitRevision() {
def cmd = "git rev-parse --short HEAD"
def cmd = "git rev-parse --short=8 HEAD"
return cmd.execute().text.trim()
}
@@ -102,7 +102,6 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.1.0-beta05"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
@@ -119,14 +118,14 @@ dependencies {
implementation "ru.noties.markwon:core:$markwon_version"
// Image
implementation 'androidx.exifinterface:exifinterface:1.0.0'
implementation 'androidx.exifinterface:exifinterface:1.1.0'
// Database
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
// Work
implementation "androidx.work:work-runtime-ktx:2.3.0-alpha01"
implementation "androidx.work:work-runtime-ktx:2.3.0-beta02"
// FP
implementation "io.arrow-kt:arrow-core:$arrow_version"

View File

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

View File

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

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.account
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.common.SessionTestParams
import im.vector.matrix.android.common.TestConstants
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class AccountCreationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
@Test
fun createAccountTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
commonTestHelper.signout(session)
session.close()
}
@Test
fun createAccountAndLoginAgainTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
// Log again to the same account
val session2 = commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
session.close()
session2.close()
}
@Test
fun simpleE2eTest() {
val res = cryptoTestHelper.doE2ETestWithAliceInARoom()
res.close()
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.auth
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.OkReplayRuleChainNoActivity
import im.vector.matrix.android.api.auth.AuthenticationService
import okreplay.*
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class AuthenticationServiceTest : InstrumentedTest {
lateinit var authenticationService: AuthenticationService
lateinit var okReplayInterceptor: OkReplayInterceptor
private val okReplayConfig = OkReplayConfig.Builder()
.tapeRoot(AndroidTapeRoot(
context(), javaClass))
.defaultMode(TapeMode.READ_WRITE) // or TapeMode.READ_ONLY
.sslEnabled(true)
.interceptor(okReplayInterceptor)
.build()
@get:Rule
val testRule = OkReplayRuleChainNoActivity(okReplayConfig).get()
@Test
@UiThreadTest
@OkReplay(tape = "auth", mode = TapeMode.READ_WRITE)
fun auth() {
}
companion object {
@ClassRule
@JvmField
val grantExternalStoragePermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}

View File

@@ -0,0 +1,289 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import android.content.Context
import android.net.Uri
import androidx.lifecycle.Observer
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.MatrixConfiguration
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.data.LoginFlowResult
import im.vector.matrix.android.api.auth.registration.RegistrationResult
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.api.session.sync.SyncState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import java.util.ArrayList
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
/**
* This class exposes methods to be used in common cases
* Registration, login, Sync, Sending messages...
*/
class CommonTestHelper(context: Context) {
val matrix: Matrix
init {
Matrix.initialize(context, MatrixConfiguration("TestFlavor"))
matrix = Matrix.getInstance(context)
}
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {
return createAccount(userNamePrefix, TestConstants.PASSWORD, testParams)
}
fun logIntoAccount(userId: String, testParams: SessionTestParams): Session {
return logIntoAccount(userId, TestConstants.PASSWORD, testParams)
}
/**
* Create a Home server configuration, with Http connection allowed for test
*/
fun createHomeServerConfig(): HomeServerConnectionConfig {
return HomeServerConnectionConfig.Builder()
.withHomeServerUri(Uri.parse(TestConstants.TESTS_HOME_SERVER_URL))
.build()
}
/**
* This methods init the event stream and check for initial sync
*
* @param session the session to sync
*/
fun syncSession(session: Session) {
val lock = CountDownLatch(1)
session.open()
session.startSync(true)
val syncLiveData = runBlocking(Dispatchers.Main) {
session.getSyncStateLive()
}
val syncObserver = object : Observer<SyncState> {
override fun onChanged(t: SyncState?) {
if (session.hasAlreadySynced()) {
lock.countDown()
syncLiveData.removeObserver(this)
}
}
}
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
await(lock)
}
/**
* Sends text messages in a room
*
* @param room the room where to send the messages
* @param message the message to send
* @param nbOfMessages the number of time the message will be sent
*/
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(nbOfMessages)
val onEventSentListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
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 })
latch.countDown()
}
}
}
val timeline = room.createTimeline(null, TimelineSettings(10))
timeline.addListener(onEventSentListener)
for (i in 0 until nbOfMessages) {
room.sendTextMessage(message + " #" + (i + 1))
}
await(latch)
timeline.removeListener(onEventSentListener)
// Check that all events has been created
assertEquals(nbOfMessages.toLong(), sentEvents.size.toLong())
return sentEvents
}
// PRIVATE METHODS *****************************************************************************
/**
* Creates a unique account
*
* @param userNamePrefix the user name prefix
* @param password the password
* @param testParams test params about the session
* @return the session associated with the newly created account
*/
private fun createAccount(userNamePrefix: String,
password: String,
testParams: SessionTestParams): Session {
val session = createAccountAndSync(
userNamePrefix + "_" + System.currentTimeMillis() + UUID.randomUUID(),
password,
testParams
)
assertNotNull(session)
return session
}
/**
* Logs into an existing account
*
* @param userId the userId to log in
* @param password the password to log in
* @param testParams test params about the session
* @return the session associated with the existing account
*/
private fun logIntoAccount(userId: String,
password: String,
testParams: SessionTestParams): Session {
val session = logAccountAndSync(userId, password, testParams)
assertNotNull(session)
return session
}
/**
* Create an account and a dedicated session
*
* @param userName the account username
* @param password the password
* @param sessionTestParams parameters for the test
*/
private fun createAccountAndSync(userName: String,
password: String,
sessionTestParams: SessionTestParams): Session {
val hs = createHomeServerConfig()
doSync<LoginFlowResult> {
matrix.authenticationService
.getLoginFlow(hs, it)
}
doSync<RegistrationResult> {
matrix.authenticationService
.getRegistrationWizard()
.createAccount(userName, password, null, it)
}
// Preform dummy step
val registrationResult = doSync<RegistrationResult> {
matrix.authenticationService
.getRegistrationWizard()
.dummy(it)
}
assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session
if (sessionTestParams.withInitialSync) {
syncSession(session)
}
return session
}
/**
* Start an account login
*
* @param userName the account username
* @param password the password
* @param sessionTestParams session test params
*/
private fun logAccountAndSync(userName: String,
password: String,
sessionTestParams: SessionTestParams): Session {
val hs = createHomeServerConfig()
doSync<LoginFlowResult> {
matrix.authenticationService
.getLoginFlow(hs, it)
}
val session = doSync<Session> {
matrix.authenticationService
.getLoginWizard()
.login(userName, password, "myDevice", it)
}
if (sessionTestParams.withInitialSync) {
syncSession(session)
}
return session
}
/**
* Await for a latch and ensure the result is true
*
* @param latch
* @throws InterruptedException
*/
fun await(latch: CountDownLatch) {
assertTrue(latch.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
}
// Transform a method with a MatrixCallback to a synchronous method
inline fun <reified T> doSync(block: (MatrixCallback<T>) -> Unit): T {
val lock = CountDownLatch(1)
var result: T? = null
val callback = object : TestMatrixCallback<T>(lock) {
override fun onSuccess(data: T) {
result = data
super.onSuccess(data)
}
}
block.invoke(callback)
await(lock)
assertNotNull(result)
return result!!
}
/**
* Clear all provided sessions
*/
fun Iterable<Session>.close() = forEach { it.close() }
fun signout(session: Session) {
val lock = CountDownLatch(1)
session.signOut(true, TestMatrixCallback(lock))
await(lock)
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import im.vector.matrix.android.api.session.Session
data class CryptoTestData(val firstSession: Session,
val roomId: String,
val secondSession: Session? = null,
val thirdSession: Session? = null) {
fun close() {
firstSession.close()
secondSession?.close()
secondSession?.close()
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import android.os.SystemClock
import androidx.lifecycle.Observer
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import java.util.Arrays
import java.util.HashMap
import java.util.concurrent.CountDownLatch
class CryptoTestHelper(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.")
val defaultSessionParams = SessionTestParams(true)
/**
* @return alice session
*/
fun doE2ETestWithAliceInARoom(): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
var roomId: String? = null
val lock1 = CountDownLatch(1)
aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), object : TestMatrixCallback<String>(lock1) {
override fun onSuccess(data: String) {
roomId = data
super.onSuccess(data)
}
})
mTestHelper.await(lock1)
assertNotNull(roomId)
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 alice and bob sessions
*/
fun doE2ETestWithAliceAndBobInARoom(): CryptoTestData {
val statuses = HashMap<String, String>()
val cryptoTestData = doE2ETestWithAliceInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
val lock1 = CountDownLatch(2)
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
}
val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) {
if (t?.isNotEmpty() == true) {
statuses["onNewRoom"] = "onNewRoom"
lock1.countDown()
bobRoomSummariesLive.removeObserver(this)
}
}
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver)
}
aliceRoom.invite(bobSession.myUserId, callback = object : TestMatrixCallback<Unit>(lock1) {
override fun onSuccess(data: Unit) {
statuses["invite"] = "invite"
super.onSuccess(data)
}
})
mTestHelper.await(lock1)
assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))
val lock2 = CountDownLatch(2)
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()
bobRoomSummariesLive.removeObserver(this)
}
}
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(roomJoinedObserver)
}
bobSession.joinRoom(aliceRoomId, callback = TestMatrixCallback(lock2))
mTestHelper.await(lock2)
// 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)
}
/**
* @return Alice, Bob and Sam session
*/
fun doE2ETestWithAliceAndBobAndSamInARoom(): CryptoTestData {
val statuses = HashMap<String, String>()
val cryptoTestData = doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val room = aliceSession.getRoom(aliceRoomId)!!
val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
val lock1 = CountDownLatch(2)
// val samEventListener = object : MXEventListener() {
// override fun onNewRoom(roomId: String) {
// if (TextUtils.equals(roomId, aliceRoomId)) {
// if (!statuses.containsKey("onNewRoom")) {
// statuses["onNewRoom"] = "onNewRoom"
// lock1.countDown()
// }
// }
// }
// }
//
// samSession.dataHandler.addListener(samEventListener)
room.invite(samSession.myUserId, null, object : TestMatrixCallback<Unit>(lock1) {
override fun onSuccess(data: Unit) {
statuses["invite"] = "invite"
super.onSuccess(data)
}
})
mTestHelper.await(lock1)
assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom"))
// samSession.dataHandler.removeListener(samEventListener)
val lock2 = CountDownLatch(1)
samSession.joinRoom(aliceRoomId, null, object : TestMatrixCallback<Unit>(lock2) {
override fun onSuccess(data: Unit) {
statuses["joinRoom"] = "joinRoom"
super.onSuccess(data)
}
})
mTestHelper.await(lock2)
assertTrue(statuses.containsKey("joinRoom"))
// wait the initial sync
SystemClock.sleep(1000)
// samSession.dataHandler.removeListener(samEventListener)
return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
}
/**
* @return Alice and Bob sessions
*/
fun doE2ETestWithAliceAndBobInARoomWithEncryptedMessages(): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val bobSession = cryptoTestData.secondSession!!
bobSession.setWarnOnUnknownDevices(false)
aliceSession.setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
var lock = CountDownLatch(1)
val bobEventsListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val size = snapshot.filter { it.root.senderId != bobSession.myUserId && it.root.getClearType() == EventType.MESSAGE }
.size
if (size == 3) {
lock.countDown()
}
}
}
val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(10))
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)
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)
return cryptoTestData
}
fun checkEncryptedEvent(event: Event, roomId: String, clearMessage: String, senderSession: Session) {
assertEquals(EventType.ENCRYPTED, event.type)
assertNotNull(event.content)
val eventWireContent = event.content.toContent()
assertNotNull(eventWireContent)
assertNull(eventWireContent.get("body"))
assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent.get("algorithm"))
assertNotNull(eventWireContent.get("ciphertext"))
assertNotNull(eventWireContent.get("session_id"))
assertNotNull(eventWireContent.get("sender_key"))
assertEquals(senderSession.sessionParams.credentials.deviceId, eventWireContent.get("device_id"))
assertNotNull(event.eventId)
assertEquals(roomId, event.roomId)
assertEquals(EventType.MESSAGE, event.getClearType())
// TODO assertTrue(event.getAge() < 10000)
val eventContent = event.toContent()
assertNotNull(eventContent)
assertEquals(clearMessage, eventContent.get("body"))
assertEquals(senderSession.myUserId, event.senderId)
}
fun createFakeMegolmBackupAuthData(): MegolmBackupAuthData {
return MegolmBackupAuthData(
publicKey = "abcdefg",
signatures = HashMap<String, Map<String, String>>().apply {
this["something"] = HashMap<String, String>().apply {
this["ed25519:something"] = "hijklmnop"
}
}
)
}
fun createFakeMegolmBackupCreationInfo(): MegolmBackupCreationInfo {
return MegolmBackupCreationInfo().apply {
algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
authData = createFakeMegolmBackupAuthData()
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import okhttp3.Interceptor
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import javax.net.ssl.HttpsURLConnection
/**
* Allows to intercept network requests for test purpose by
* - re-writing the response
* - changing the response code (200/404/etc..).
* - Test delays..
*
* Basic usage:
* <code>
* val mockInterceptor = MockOkHttpInterceptor()
* mockInterceptor.addRule(MockOkHttpInterceptor.SimpleRule(".well-known/matrix/client", 200, "{}"))
*
* RestHttpClientFactoryProvider.defaultProvider = RestClientHttpClientFactory(mockInterceptor)
* AutoDiscovery().findClientConfig("matrix.org", <callback>)
* </code>
*/
class MockOkHttpInterceptor : Interceptor {
private var rules: ArrayList<Rule> = ArrayList()
fun addRule(rule: Rule) {
rules.add(rule)
}
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
rules.forEach { rule ->
if (originalRequest.url.toString().contains(rule.match)) {
rule.process(originalRequest)?.let {
return it
}
}
}
return chain.proceed(originalRequest)
}
abstract class Rule(val match: String) {
abstract fun process(originalRequest: Request): Response?
}
/**
* Simple rule that reply with the given body for any request that matches the match param
*/
class SimpleRule(match: String,
private val code: Int = HttpsURLConnection.HTTP_OK,
private val body: String = "{}") : Rule(match) {
override fun process(originalRequest: Request): Response? {
return Response.Builder()
.protocol(Protocol.HTTP_1_1)
.request(originalRequest)
.message("mocked answer")
.body(body.toResponseBody(null))
.code(code)
.build()
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
data class SessionTestParams @JvmOverloads constructor(val withInitialSync: Boolean = false)

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import org.junit.Assert.*
/**
* Compare two lists and their content
*/
fun assertListEquals(list1: List<Any>?, list2: List<Any>?) {
if (list1 == null) {
assertNull(list2)
} else {
assertNotNull(list2)
assertEquals("List sizes must match", list1.size, list2!!.size)
for (i in list1.indices) {
assertEquals("Elements at index $i are not equal", list1[i], list2[i])
}
}
}
/**
* Compare two maps and their content
*/
fun assertDictEquals(dict1: Map<String, Any>?, dict2: Map<String, Any>?) {
if (dict1 == null) {
assertNull(dict2)
} else {
assertNotNull(dict2)
assertEquals("Map sizes must match", dict1.size, dict2!!.size)
for (i in dict1.keys) {
assertEquals("Values for key $i are not equal", dict1[i], dict2[i])
}
}
}
/**
* Compare two byte arrays content.
* Note that if the arrays have not the same size, it also fails.
*/
fun assertByteArrayNotEqual(a1: ByteArray, a2: ByteArray) {
if (a1.size != a2.size) {
fail("Arrays have not the same size.")
}
for (index in a1.indices) {
if (a1[index] != a2[index]) {
// Difference found!
return
}
}
fail("Arrays are equals.")
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import android.os.Debug
object TestConstants {
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
// Time out to use when waiting for server response. 60s
private const val AWAIT_TIME_OUT_MILLIS = 60000
// 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
const val USER_ALICE = "Alice"
const val USER_BOB = "Bob"
const val USER_SAM = "Sam"
const val PASSWORD = "password"
val timeOutMillis: Long
get() = if (Debug.isDebuggerConnected()) {
// Wait more
AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS.toLong()
} else {
AWAIT_TIME_OUT_MILLIS.toLong()
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.common
import androidx.annotation.CallSuper
import im.vector.matrix.android.api.MatrixCallback
import org.junit.Assert.fail
import timber.log.Timber
import java.util.concurrent.CountDownLatch
/**
* Simple implementation of MatrixCallback, which count down the CountDownLatch on each API callback
* @param onlySuccessful true to fail if an error occurs. This is the default behavior
* @param <T>
*/
open class TestMatrixCallback<T>(private val countDownLatch: CountDownLatch,
private val onlySuccessful: Boolean = true) : MatrixCallback<T> {
@CallSuper
override fun onSuccess(data: T) {
countDownLatch.countDown()
}
@CallSuper
override fun onFailure(failure: Throwable) {
Timber.e(failure, "TestApiCallback")
if (onlySuccessful) {
fail("onFailure " + failure.localizedMessage)
}
countDownLatch.countDown()
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto
import android.os.MemoryFile
import android.util.Base64
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey
import org.junit.Assert.*
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import java.io.ByteArrayInputStream
import java.io.InputStream
/**
* Unit tests AttachmentEncryptionTest.
*/
@Suppress("SpellCheckingInspection")
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AttachmentEncryptionTest {
private fun checkDecryption(input: String, encryptedFileInfo: EncryptedFileInfo): String {
val `in` = Base64.decode(input, Base64.DEFAULT)
val inputStream: InputStream
inputStream = if (`in`.isEmpty()) {
ByteArrayInputStream(`in`)
} else {
val memoryFile = MemoryFile("file" + System.currentTimeMillis(), `in`.size)
memoryFile.outputStream.write(`in`)
memoryFile.inputStream
}
val decryptedStream = MXEncryptedAttachments.decryptAttachment(inputStream, encryptedFileInfo)
assertNotNull(decryptedStream)
val buffer = ByteArray(100)
val len = decryptedStream!!.read(buffer)
decryptedStream.close()
return Base64.encodeToString(buffer, 0, len, Base64.DEFAULT).replace("\n".toRegex(), "").replace("=".toRegex(), "")
}
@Test
fun checkDecrypt1() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "AAAAAAAAAAAAAAAAAAAAAA",
url = "dummyUrl"
)
assertEquals("", checkDecryption("", encryptedFileInfo))
}
@Test
fun checkDecrypt2() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "YzF08lARDdOCzJpzuSwsjTNlQc4pHxpdHcXiD/wpK6k"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "__________________________________________8",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "//////////8AAAAAAAAAAA",
url = "dummyUrl"
)
assertEquals("SGVsbG8sIFdvcmxk", checkDecryption("5xJZTt5cQicm+9f4", encryptedFileInfo))
}
@Test
fun checkDecrypt3() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "IOq7/dHHB+mfHfxlRY5XMeCWEwTPmlf4cJcgrkf6fVU"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "__________________________________________8",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "//////////8AAAAAAAAAAA",
url = "dummyUrl"
)
assertEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption("zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q",
encryptedFileInfo))
}
@Test
fun checkDecrypt4() {
val encryptedFileInfo = EncryptedFileInfo(
v = "v2",
hashes = mapOf("sha256" to "LYG/orOViuFwovJpv2YMLSsmVKwLt7pY3f8SYM7KU5E"),
key = EncryptedFileKey(
alg = "A256CTR",
k = "__________________________________________8",
key_ops = listOf("encrypt", "decrypt"),
kty = "oct",
ext = true
),
iv = "/////////////////////w",
url = "dummyUrl"
)
assertNotEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption("tJVNBVJ/vl36UQt4Y5e5m84bRUrQHhcdLPvS/7EkDvlkDLZXamBB6k8THbiawiKZ5Mnq9PZMSSbgOCvmnUBOMA",
encryptedFileInfo))
}
}

View File

@@ -16,20 +16,31 @@
package im.vector.matrix.android.internal.crypto
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import io.realm.Realm
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmManager
import org.matrix.olm.OlmSession
private const val DUMMY_DEVICE_KEY = "DeviceKey"
class CryptoStoreTest {
@RunWith(AndroidJUnit4::class)
class CryptoStoreTest : InstrumentedTest {
private val cryptoStoreHelper = CryptoStoreHelper()
@Before
fun setup() {
Realm.init(context())
}
@Test
fun test_metadata_realm_ok() {
val cryptoStore: IMXCryptoStore = cryptoStoreHelper.createStore()

View File

@@ -0,0 +1,206 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.*
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
/**
* Unit tests ExportEncryptionTest.
*/
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ExportEncryptionTest {
@Test
fun checkExportError1() {
val password = "password"
val input = "-----"
var failed = false
try {
MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
failed = true
}
assertTrue(failed)
}
@Test
fun checkExportError2() {
val password = "password"
val input = "-----BEGIN MEGOLM SESSION DATA-----\n" + "-----"
var failed = false
try {
MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
failed = true
}
assertTrue(failed)
}
@Test
fun checkExportError3() {
val password = "password"
val input = "-----BEGIN MEGOLM SESSION DATA-----\n" +
" AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" +
" cissyYBxjsfsAn\n" +
" -----END MEGOLM SESSION DATA-----"
var failed = false
try {
MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
failed = true
}
assertTrue(failed)
}
@Test
fun checkExportDecrypt1() {
val password = "password"
val input = "-----BEGIN MEGOLM SESSION DATA-----\nAXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" + "cissyYBxjsfsAndErh065A8=\n-----END MEGOLM SESSION DATA-----"
val expectedString = "plain"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
fail("## checkExportDecrypt1() failed : " + e.message)
}
assertEquals("## checkExportDecrypt1() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportDecrypt2() {
val password = "betterpassword"
val input = "-----BEGIN MEGOLM SESSION DATA-----\nAW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" + "KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n-----END MEGOLM SESSION DATA-----"
val expectedString = "Hello, World"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
fail("## checkExportDecrypt2() failed : " + e.message)
}
assertEquals("## checkExportDecrypt2() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportDecrypt3() {
val password = "SWORDFISH"
val input = "-----BEGIN MEGOLM SESSION DATA-----\nAXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" + "MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\nPgg29363BGR+/Ripq/VCLKGNbw==\n-----END MEGOLM SESSION DATA-----"
val expectedString = "alphanumericallyalphanumericallyalphanumericallyalphanumerically"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption.decryptMegolmKeyFile(input.toByteArray(charset("UTF-8")), password)
} catch (e: Exception) {
fail("## checkExportDecrypt3() failed : " + e.message)
}
assertEquals("## checkExportDecrypt3() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt1() {
val password = "password"
val expectedString = "plain"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt1() failed : " + e.message)
}
assertEquals("## checkExportEncrypt1() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt2() {
val password = "betterpassword"
val expectedString = "Hello, World"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt2() failed : " + e.message)
}
assertEquals("## checkExportEncrypt2() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt3() {
val password = "SWORDFISH"
val expectedString = "alphanumericallyalphanumericallyalphanumericallyalphanumerically"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt3() failed : " + e.message)
}
assertEquals("## checkExportEncrypt3() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
@Test
fun checkExportEncrypt4() {
val password = "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword"
val expectedString = "alphanumericallyalphanumericallyalphanumericallyalphanumerically"
var decodedString: String? = null
try {
decodedString = MXMegolmExportEncryption
.decryptMegolmKeyFile(MXMegolmExportEncryption.encryptMegolmKeyFile(expectedString, password, 1000), password)
} catch (e: Exception) {
fail("## checkExportEncrypt4() failed : " + e.message)
}
assertEquals("## checkExportEncrypt4() : expectedString $expectedString -- decodedString $decodedString",
expectedString,
decodedString)
}
}

View File

@@ -0,0 +1,209 @@
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
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class XSigningTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Test
fun test_InitializeAndStoreKeys() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val aliceLatch = CountDownLatch(1)
aliceSession.getCrossSigningService()
.initializeCrossSigning(UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
), TestMatrixCallback(aliceLatch))
mTestHelper.await(aliceLatch)
val myCrossSigningKeys = aliceSession.getCrossSigningService().getMyCrossSigningKeys()
val masterPubKey = myCrossSigningKeys?.masterKey()
assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey)
val selfSigningKey = myCrossSigningKeys?.selfSigningKey()
assertNotNull("SelfSigned key should be stored", selfSigningKey?.unpaddedBase64PublicKey)
val userKey = myCrossSigningKeys?.userKey()
assertNotNull("User key should be stored", userKey?.unpaddedBase64PublicKey)
assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted() == true)
assertTrue("Signing Keys should be trusted", aliceSession.getCrossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
mTestHelper.signout(aliceSession)
}
@Test
fun test_CrossSigningCheckBobSeesTheKeys() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceAuthParams = UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
)
val bobAuthParams = UserPasswordAuth(
user = bobSession!!.myUserId,
password = TestConstants.PASSWORD
)
val latch = CountDownLatch(2)
aliceSession.getCrossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
bobSession.getCrossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
mTestHelper.await(latch)
// Check that alice can see bob keys
val downloadLatch = CountDownLatch(1)
aliceSession.downloadKeys(listOf(bobSession.myUserId), true, TestMatrixCallback(downloadLatch))
mTestHelper.await(downloadLatch)
val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().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)
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
mTestHelper.signout(aliceSession)
mTestHelper.signout(bobSession)
}
@Test
fun test_CrossSigningTestAliceTrustBobNewDevice() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceAuthParams = UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD
)
val bobAuthParams = UserPasswordAuth(
user = bobSession!!.myUserId,
password = TestConstants.PASSWORD
)
val latch = CountDownLatch(2)
aliceSession.getCrossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
bobSession.getCrossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
mTestHelper.await(latch)
// 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)
val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().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)
// Now bobs logs in on a new device and verifies it
// We will want to test that in alice POV, this new device would be trusted by cross signing
val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true))
val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId
// Check that bob first session sees the new login
val bobKeysLatch = CountDownLatch(1)
bobSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback<MXUsersDevicesMap<CryptoDeviceInfo>> {
override fun onFailure(failure: Throwable) {
fail("Failed to get device")
}
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)
val bobSecondDevicePOVFirstDevice = bobSession.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)
// 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")
}
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)
val result = aliceSession.getCrossSigningService().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)
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.keysbackup
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.common.assertByteArrayNotEqual
import org.junit.Assert.*
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.matrix.olm.OlmManager
import org.matrix.olm.OlmPkDecryption
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class KeysBackupPasswordTest : InstrumentedTest {
@Before
fun ensureLibLoaded() {
OlmManager()
}
/**
* Check KeysBackupPassword utilities
*/
@Test
fun passwordConverter_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation
val retrievedPrivateKey = retrievePrivateKeyWithPassword(PASSWORD,
generatePrivateKeyResult.salt,
generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertArrayEquals(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check generatePrivateKeyWithPassword progress listener behavior
*/
@Test
fun passwordConverter_progress_ok() {
val progressValues = ArrayList<Int>(101)
var lastTotal = 0
generatePrivateKeyWithPassword(PASSWORD, object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
if (!progressValues.contains(progress)) {
progressValues.add(progress)
}
lastTotal = total
}
})
assertEquals(100, lastTotal)
// Ensure all values are here
assertEquals(101, progressValues.size)
for (i in 0..100) {
assertTrue(progressValues[i] == i)
}
}
/**
* Check KeysBackupPassword utilities, with bad password
*/
@Test
fun passwordConverter_badPassword_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation, with bad password
val retrievedPrivateKey = retrievePrivateKeyWithPassword(BAD_PASSWORD,
generatePrivateKeyResult.salt,
generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check KeysBackupPassword utilities, with bad password
*/
@Test
fun passwordConverter_badIteration_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation, with bad iteration
val retrievedPrivateKey = retrievePrivateKeyWithPassword(PASSWORD,
generatePrivateKeyResult.salt,
500_001)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check KeysBackupPassword utilities, with bad salt
*/
@Test
fun passwordConverter_badSalt_ok() {
val generatePrivateKeyResult = generatePrivateKeyWithPassword(PASSWORD, null)
assertEquals(32, generatePrivateKeyResult.salt.length)
assertEquals(500_000, generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
// Reverse operation, with bad iteration
val retrievedPrivateKey = retrievePrivateKeyWithPassword(PASSWORD,
BAD_SALT,
generatePrivateKeyResult.iterations)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
}
/**
* Check [retrievePrivateKeyWithPassword] with data coming from another platform (RiotWeb).
*/
@Test
fun passwordConverter_crossPlatform_ok() {
val password = "This is a passphrase!"
val salt = "TO0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"
val iteration = 500_000
val retrievedPrivateKey = retrievePrivateKeyWithPassword(password, salt, iteration)
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
// Data from RiotWeb
val privateKeyBytes = byteArrayOf(
116.toByte(), 224.toByte(), 229.toByte(), 224.toByte(), 9.toByte(), 3.toByte(), 178.toByte(), 162.toByte(),
120.toByte(), 23.toByte(), 108.toByte(), 218.toByte(), 22.toByte(), 61.toByte(), 241.toByte(), 200.toByte(),
235.toByte(), 173.toByte(), 236.toByte(), 100.toByte(), 115.toByte(), 247.toByte(), 33.toByte(), 132.toByte(),
195.toByte(), 154.toByte(), 64.toByte(), 158.toByte(), 184.toByte(), 148.toByte(), 20.toByte(), 85.toByte())
assertArrayEquals(privateKeyBytes, retrievedPrivateKey)
}
companion object {
private const val PASSWORD = "password"
private const val BAD_PASSWORD = "passw0rd"
private const val BAD_SALT = "AA0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.keysbackup
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import java.util.concurrent.CountDownLatch
/**
* This class observe the state change of a KeysBackup object and provide a method to check the several state change
* It checks all state transitions and detected forbidden transition
*/
internal class StateObserver(private val keysBackup: KeysBackupService,
private val latch: CountDownLatch? = null,
private val expectedStateChange: Int = -1) : KeysBackupStateListener {
private val allowedStateTransitions = listOf(
KeysBackupState.BackingUp to KeysBackupState.ReadyToBackUp,
KeysBackupState.BackingUp to KeysBackupState.WrongBackUpVersion,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.Disabled,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.NotTrusted,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.ReadyToBackUp,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.Unknown,
KeysBackupState.CheckingBackUpOnHomeserver to KeysBackupState.WrongBackUpVersion,
KeysBackupState.Disabled to KeysBackupState.Enabling,
KeysBackupState.Enabling to KeysBackupState.Disabled,
KeysBackupState.Enabling to KeysBackupState.ReadyToBackUp,
KeysBackupState.NotTrusted to KeysBackupState.CheckingBackUpOnHomeserver,
// This transition happens when we trust the device
KeysBackupState.NotTrusted to KeysBackupState.ReadyToBackUp,
KeysBackupState.ReadyToBackUp to KeysBackupState.WillBackUp,
KeysBackupState.Unknown to KeysBackupState.CheckingBackUpOnHomeserver,
KeysBackupState.WillBackUp to KeysBackupState.BackingUp,
KeysBackupState.WrongBackUpVersion to KeysBackupState.CheckingBackUpOnHomeserver,
// FIXME These transitions are observed during test, and I'm not sure they should occur. Don't have time to investigate now
KeysBackupState.ReadyToBackUp to KeysBackupState.BackingUp,
KeysBackupState.ReadyToBackUp to KeysBackupState.ReadyToBackUp,
KeysBackupState.WillBackUp to KeysBackupState.ReadyToBackUp,
KeysBackupState.WillBackUp to KeysBackupState.Unknown
)
private val stateList = ArrayList<KeysBackupState>()
private var lastTransitionError: String? = null
init {
keysBackup.addListener(this)
}
// TODO Make expectedStates mandatory to enforce test
fun stopAndCheckStates(expectedStates: List<KeysBackupState>?) {
keysBackup.removeListener(this)
expectedStates?.let {
assertEquals(it.size, stateList.size)
for (i in it.indices) {
assertEquals("The state $i is not correct. states: " + stateList.joinToString(separator = " "), it[i], stateList[i])
}
}
assertNull("states: " + stateList.joinToString(separator = " "), lastTransitionError)
}
override fun onStateChange(newState: KeysBackupState) {
stateList.add(newState)
// Check that state transition is valid
if (stateList.size >= 2
&& !allowedStateTransitions.contains(stateList[stateList.size - 2] to newState)) {
// Forbidden transition detected
lastTransitionError = "Forbidden transition detected from " + stateList[stateList.size - 2] + " to " + newState
}
if (expectedStateChange == stateList.size) {
latch?.countDown()
}
}
}

View File

@@ -0,0 +1,561 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.crypto.verification
import androidx.test.ext.junit.runners.AndroidJUnit4
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.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.events.model.Event
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.common.CommonTestHelper
import im.vector.matrix.android.common.CryptoTestHelper
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
import im.vector.matrix.android.internal.crypto.model.rest.toValue
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SASTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Test
fun test_aliceStartThenAliceCancel() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val bobTxCreatedLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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,
null)
assertNotNull("Alice should have a started transaction", txID)
val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!)
assertNotNull("Alice should have a started transaction", aliceKeyTx)
mTestHelper.await(bobTxCreatedLatch)
bobVerificationService.removeListener(bobListener)
val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)
assertNotNull("Bob should have started verif transaction", bobKeyTx)
assertTrue(bobKeyTx is SASDefaultVerificationTransaction)
assertNotNull("Bob should have starting a SAS transaction", bobKeyTx)
assertTrue(aliceKeyTx is SASDefaultVerificationTransaction)
assertEquals("Alice and Bob have same transaction id", aliceKeyTx!!.transactionId, bobKeyTx!!.transactionId)
val aliceSasTx = aliceKeyTx as SASDefaultVerificationTransaction?
val bobSasTx = bobKeyTx as SASDefaultVerificationTransaction?
assertEquals("Alice state should be started", VerificationTxState.Started, aliceSasTx!!.state)
assertEquals("Bob state should be started by alice", VerificationTxState.OnStarted, bobSasTx!!.state)
// Let's cancel from alice side
val cancelLatch = CountDownLatch(1)
val bobListener2 = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.transactionId == txID) {
val immutableState = (tx as SASDefaultVerificationTransaction).state
if (immutableState is VerificationTxState.Cancelled && !immutableState.byMe) {
cancelLatch.countDown()
}
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener2)
aliceSasTx.cancel(CancelCode.User)
mTestHelper.await(cancelLatch)
assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled)
assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled)
val aliceCancelState = aliceSasTx.state as VerificationTxState.Cancelled
val bobCancelState = bobSasTx.state as VerificationTxState.Cancelled
assertTrue("Should be cancelled by me on alice side", aliceCancelState.byMe)
assertFalse("Should be cancelled by other on bob side", bobCancelState.byMe)
assertEquals("Should be User cancelled on alice side", CancelCode.User, aliceCancelState.cancelCode)
assertEquals("Should be User cancelled on bob side", CancelCode.User, bobCancelState.cancelCode)
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
cryptoTestData.close()
}
@Test
fun test_key_agreement_protocols_must_include_curve25519() {
fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
val protocols = listOf("meh_dont_know")
val tid = "00000000"
// Bob should receive a cancel
var cancelReason: CancelCode? = null
val cancelLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
// TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) {
// TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
// TODO canceledToDeviceEvent = event
// TODO cancelLatch.countDown()
// TODO }
// TODO }
// TODO }
// TODO })
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.getMyDevice().deviceId
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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)
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
mTestHelper.await(cancelLatch)
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
cryptoTestData.close()
}
@Test
fun test_key_agreement_macs_Must_include_hmac_sha256() {
fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
val mac = listOf("shaBit")
val tid = "00000000"
// Bob should receive a cancel
var canceledToDeviceEvent: Event? = null
val cancelLatch = CountDownLatch(1)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
// TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) {
// TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
// TODO canceledToDeviceEvent = event
// TODO cancelLatch.countDown()
// TODO }
// TODO }
// TODO }
// TODO })
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.getMyDevice().deviceId
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
mTestHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.close()
}
@Test
fun test_key_agreement_short_code_include_decimal() {
fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
val codes = listOf("bin", "foo", "bar")
val tid = "00000000"
// Bob should receive a cancel
var canceledToDeviceEvent: Event? = null
val cancelLatch = CountDownLatch(1)
// TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
// TODO override fun onToDeviceEvent(event: Event?) {
// TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) {
// TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
// TODO canceledToDeviceEvent = event
// TODO cancelLatch.countDown()
// TODO }
// TODO }
// TODO }
// TODO })
val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.getMyDevice().deviceId
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
mTestHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.close()
}
private fun fakeBobStart(bobSession: Session,
aliceUserID: String?,
aliceDevice: String?,
tid: String,
protocols: List<String> = SASDefaultVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS,
hashes: List<String> = SASDefaultVerificationTransaction.KNOWN_HASHES,
mac: List<String> = SASDefaultVerificationTransaction.KNOWN_MACS,
codes: List<String> = SASDefaultVerificationTransaction.KNOWN_SHORT_CODES) {
val startMessage = KeyVerificationStart(
fromDevice = bobSession.getMyDevice().deviceId,
method = VerificationMethod.SAS.toValue(),
transactionID = tid,
keyAgreementProtocols = protocols,
hashes = hashes,
messageAuthenticationCodes = mac,
shortAuthenticationStrings = codes
)
val contentMap = MXUsersDevicesMap<Any>()
contentMap.setObject(aliceUserID, aliceDevice, startMessage)
// TODO val sendLatch = CountDownLatch(1)
// TODO bobSession.cryptoRestClient.sendToDevice(
// TODO EventType.KEY_VERIFICATION_START,
// TODO contentMap,
// TODO tid,
// TODO TestMatrixCallback<Void>(sendLatch)
// TODO )
}
// any two devices may only have at most one key verification in flight at a time.
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
@Test
fun test_aliceStartTwoRequests() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val aliceCreatedLatch = CountDownLatch(2)
val aliceCancelledLatch = CountDownLatch(2)
val createdTx = mutableListOf<SASDefaultVerificationTransaction>()
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {
createdTx.add(tx as SASDefaultVerificationTransaction)
aliceCreatedLatch.countDown()
}
override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as SASDefaultVerificationTransaction).state is VerificationTxState.Cancelled && !(tx.state as VerificationTxState.Cancelled).byMe) {
aliceCancelledLatch.countDown()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceVerificationService.addListener(aliceListener)
val bobUserId = bobSession!!.myUserId
val bobDeviceId = bobSession.getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceCreatedLatch)
mTestHelper.await(aliceCancelledLatch)
cryptoTestData.close()
}
/**
* Test that when alice starts a 'correct' request, bob agrees.
*/
@Test
fun test_aliceAndBobAgreement() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
var accepted: KeyVerificationAccept? = null
var startReq: KeyVerificationStart? = null
val aliceAcceptedLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.VerificationListener {
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
override fun transactionCreated(tx: VerificationTransaction) {}
override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) {
val at = tx as SASDefaultVerificationTransaction
accepted = at.accepted as? KeyVerificationAccept
startReq = at.startReq as? KeyVerificationStart
aliceAcceptedLatch.countDown()
}
}
}
aliceVerificationService.addListener(aliceListener)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
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
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceAcceptedLatch)
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false)
// check that agreement is valid
assertTrue("Agreed Protocol should be Valid", accepted!!.isValid())
assertTrue("Agreed Protocol should be known by alice", startReq!!.keyAgreementProtocols!!.contains(accepted!!.keyAgreementProtocol))
assertTrue("Hash should be known by alice", startReq!!.hashes!!.contains(accepted!!.hash))
assertTrue("Hash should be known by alice", startReq!!.messageAuthenticationCodes!!.contains(accepted!!.messageAuthenticationCode))
accepted!!.shortAuthenticationStrings?.forEach {
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings!!.contains(it))
}
cryptoTestData.close()
}
@Test
fun test_aliceAndBobSASCode() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
when (uxState) {
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
aliceSASLatch.countDown()
}
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) {}
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
tx.performAccept()
}
else -> Unit
}
if (uxState === IncomingSasVerificationTransaction.UxState.SHOW_SAS) {
bobSASLatch.countDown()
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.getMyDevice().deviceId
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceSASLatch)
mTestHelper.await(bobSASLatch)
val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction
val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction
assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
cryptoTestData.close()
}
@Test
fun test_happyPath() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
val aliceVerificationService = aliceSession.getVerificationService()
val bobVerificationService = bobSession!!.getVerificationService()
val aliceSASLatch = CountDownLatch(1)
val aliceListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as OutgoingSasVerificationTransaction).uxState
when (uxState) {
OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> {
tx.userHasVerifiedShortCode()
}
OutgoingSasVerificationTransaction.UxState.VERIFIED -> {
aliceSASLatch.countDown()
}
else -> Unit
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceVerificationService.addListener(aliceListener)
val bobSASLatch = CountDownLatch(1)
val bobListener = object : VerificationService.VerificationListener {
override fun transactionCreated(tx: VerificationTransaction) {}
override fun transactionUpdated(tx: VerificationTransaction) {
val uxState = (tx as IncomingSasVerificationTransaction).uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
tx.performAccept()
}
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
tx.userHasVerifiedShortCode()
}
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
bobSASLatch.countDown()
}
else -> Unit
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
bobVerificationService.addListener(bobListener)
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.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)
// 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()
}
}

View File

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

View File

@@ -19,8 +19,12 @@ package im.vector.matrix.android.session.room.timeline
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.InstrumentedTest
import im.vector.matrix.android.internal.database.helper.*
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.database.helper.add
import im.vector.matrix.android.internal.database.helper.lastStateIndex
import im.vector.matrix.android.internal.database.helper.merge
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
@@ -28,7 +32,6 @@ import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeR
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.createObject
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue
import org.amshove.kluent.shouldEqual
import org.junit.Before
@@ -43,7 +46,11 @@ internal class ChunkEntityTest : InstrumentedTest {
@Before
fun setup() {
Realm.init(context())
val testConfig = RealmConfiguration.Builder().inMemory().name("test-realm").build()
val testConfig = RealmConfiguration.Builder()
.inMemory()
.name("test-realm")
.modules(SessionRealmModule())
.build()
monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
}
@@ -141,30 +148,6 @@ internal class ChunkEntityTest : InstrumentedTest {
}
}
@Test
fun merge_shouldEventsBeLinked_whenMergingLinkedWithUnlinked() {
monarchy.runTransactionSync { realm ->
val chunk1: ChunkEntity = realm.createObject()
val chunk2: ChunkEntity = realm.createObject()
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = false)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.isUnlinked().shouldBeFalse()
}
}
@Test
fun merge_shouldEventsBeUnlinked_whenMergingUnlinkedWithUnlinked() {
monarchy.runTransactionSync { realm ->
val chunk1: ChunkEntity = realm.createObject()
val chunk2: ChunkEntity = realm.createObject()
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.isUnlinked().shouldBeTrue()
}
}
@Test
fun merge_shouldPrevTokenMerged_whenMergingForwards() {
monarchy.runTransactionSync { realm ->
@@ -172,8 +155,8 @@ internal class ChunkEntityTest : InstrumentedTest {
val chunk2: ChunkEntity = realm.createObject()
val prevToken = "prev_token"
chunk1.prevToken = prevToken
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge("roomId", chunk2, PaginationDirection.FORWARDS)
chunk1.prevToken shouldEqual prevToken
}
@@ -186,10 +169,19 @@ internal class ChunkEntityTest : InstrumentedTest {
val chunk2: ChunkEntity = realm.createObject()
val nextToken = "next_token"
chunk1.nextToken = nextToken
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
chunk1.nextToken shouldEqual nextToken
}
}
private fun ChunkEntity.addAll(roomId: String,
events: List<Event>,
direction: PaginationDirection,
stateIndexOffset: Int = 0) {
events.forEach { event ->
add(roomId, event, direction, stateIndexOffset)
}
}
}

View File

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

View File

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

View File

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

View File

@@ -24,10 +24,13 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
import im.vector.matrix.android.internal.di.DaggerMatrixComponent
import im.vector.matrix.android.internal.network.UserAgentHolder
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import org.matrix.olm.OlmManager
import java.io.InputStream
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
@@ -96,5 +99,9 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
fun getSdkVersion(): String {
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
}
fun decryptStream(inputStream: InputStream?, elementToDecrypt: ElementToDecrypt): InputStream? {
return MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt)
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.crypto
/**
* RoomEncryptionTrustLevel represents the trust level in an encrypted room.
*/
enum class RoomEncryptionTrustLevel {
// No one in the room has been verified -> Black shield
Default,
// There are one or more device un-verified -> the app should display a red shield
Warning,
// All devices in the room are verified -> the app should display a green shield
Trusted
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.extensions
fun Boolean?.orTrue() = this ?: true
fun Boolean?.orFalse() = this ?: false

View File

@@ -17,17 +17,23 @@
package im.vector.matrix.android.api.extensions
import im.vector.matrix.android.api.comparators.DatedObjectComparators
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
/* ==========================================================================================
* MXDeviceInfo
* ========================================================================================== */
fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint()
fun CryptoDeviceInfo.getFingerprintHumanReadable() = fingerprint()
?.chunked(4)
?.joinToString(separator = " ")
fun MutableList<DeviceInfo>.sortByLastSeen() {
sortWith(DatedObjectComparators.descComparator)
/* ==========================================================================================
* DeviceInfo
* ========================================================================================== */
fun List<DeviceInfo>.sortByLastSeen(): List<DeviceInfo> {
val list = toMutableList()
list.sortWith(DatedObjectComparators.descComparator)
return list
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.failure
import javax.net.ssl.HttpsURLConnection
fun Throwable.is401() =
this is Failure.ServerError
&& httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */
&& error.code == MatrixError.M_UNAUTHORIZED
fun Throwable.isTokenError() =
this is Failure.ServerError
&& (error.code == MatrixError.M_UNKNOWN_TOKEN || error.code == MatrixError.M_MISSING_TOKEN)
fun Throwable.shouldBeRetried(): Boolean {
return this is Failure.NetworkConnection
|| (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED)
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.failure
// This class will be sent to the bus
sealed class GlobalError {
data class InvalidToken(val softLogout: Boolean) : GlobalError()
data class ConsentNotGivenError(val consentUri: String) : GlobalError()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,8 @@
package im.vector.matrix.android.api.pushrules
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.PowerLevels
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
@@ -28,7 +29,8 @@ class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.se
return "User power level <$key>"
}
fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key)
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
val powerLevelsHelper = PowerLevelsHelper(powerLevels)
return event.senderId != null && powerLevelsHelper.getUserPowerLevel(event.senderId) >= powerLevelsHelper.notificationLevel(key)
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.query
/**
* Basic query language. All these cases are mutually exclusive.
*/
sealed class QueryStringValue {
object NoCondition : QueryStringValue()
object IsNull : QueryStringValue()
object IsNotNull : QueryStringValue()
object IsEmpty : QueryStringValue()
object IsNotEmpty : QueryStringValue()
data class Equals(val string: String, val case: Case) : QueryStringValue()
data class Contains(val string: String, val case: Case) : QueryStringValue()
enum class Case {
SENSITIVE,
INSENSITIVE
}
}

View File

@@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session
import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.failure.ConsentNotGivenError
import im.vector.matrix.android.api.failure.GlobalError
import im.vector.matrix.android.api.pushrules.PushRuleService
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
@@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.profile.ProfileService
import im.vector.matrix.android.api.session.pushers.PushersService
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
@@ -51,6 +52,7 @@ interface Session :
SignOutService,
FilterService,
FileService,
ProfileService,
PushRuleService,
PushersService,
InitialSyncProgressService,
@@ -62,12 +64,22 @@ interface Session :
*/
val sessionParams: SessionParams
/**
* The session is valid, i.e. it has a valid token so far
*/
val isOpenable: Boolean
/**
* Useful shortcut to get access to the userId
*/
val myUserId: String
get() = sessionParams.credentials.userId
/**
* The sessionId
*/
val sessionId: String
/**
* This method allow to open a session. It does start some service on the background.
*/
@@ -81,7 +93,7 @@ interface Session :
/**
* Launches infinite periodic background syncs
* THis does not work in doze mode :/
* This does not work in doze mode :/
* If battery optimization is on it can work in app standby but that's all :/
*/
fun startAutomaticBackgroundSync(repeatDelay: Long = 30_000L)
@@ -102,7 +114,12 @@ interface Session :
* This method allows to listen the sync state.
* @return a [LiveData] of [SyncState].
*/
fun syncState(): LiveData<SyncState>
fun getSyncStateLive(): LiveData<SyncState>
/**
* This methods return true if an initial sync has been processed
*/
fun hasAlreadySynced(): Boolean
/**
* This method allow to close a session. It does stop some services.
@@ -136,13 +153,10 @@ interface Session :
*/
interface Listener {
/**
* The access token is not valid anymore
* Possible cases:
* - The access token is not valid anymore,
* - a M_CONSENT_NOT_GIVEN error has been received from the homeserver
*/
fun onInvalidToken()
/**
* A M_CONSENT_NOT_GIVEN error has been received from the homeserver
*/
fun onConsentNotGivenError(consentNotGivenError: ConsentNotGivenError)
fun onGlobalError(globalError: GlobalError)
}
}

View File

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

View File

@@ -30,7 +30,7 @@ data class ContentAttachmentData(
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
val name: String? = null,
val path: String,
val mimeType: String,
val mimeType: String?,
val type: Type
) : Parcelable {

View File

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

View File

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

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.crosssigning
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.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
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
interface CrossSigningService {
fun isCrossSigningVerified(): Boolean
fun isUserTrusted(otherUserId: String): Boolean
/**
* Will not force a download of the key, but will verify signatures trust chain.
* Checks that my trusted user key has signed the other user UserKey
*/
fun checkUserTrust(otherUserId: String): UserTrustResult
/**
* Initialize cross signing for this user.
* Users needs to enter credentials
*/
fun initializeCrossSigning(authParams: UserPasswordAuth?,
callback: MatrixCallback<Unit>? = null)
fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo?
fun getLiveCrossSigningKeys(userId: String): LiveData<Optional<MXCrossSigningInfo>>
fun getMyCrossSigningKeys(): MXCrossSigningInfo?
fun canCrossSign(): Boolean
fun trustUser(otherUserId: String,
callback: MatrixCallback<Unit>)
/**
* Sign one of your devices and upload the signature
*/
fun signDevice(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,38 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.crosssigning
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.KeyUsage
data class MXCrossSigningInfo(
val userId: String,
val crossSigningKeys: List<CryptoCrossSigningKey>
) {
fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true
&& selfSigningKey()?.trustLevel?.isVerified() == true
fun masterKey(): CryptoCrossSigningKey? = crossSigningKeys
.firstOrNull { it.usages?.contains(KeyUsage.MASTER.value) == true }
fun userKey(): CryptoCrossSigningKey? = crossSigningKeys
.firstOrNull { it.usages?.contains(KeyUsage.USER_SIGNING.value) == true }
fun selfSigningKey(): CryptoCrossSigningKey? = crossSigningKeys
.firstOrNull { it.usages?.contains(KeyUsage.SELF_SIGNING.value) == true }
}

View File

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

View File

@@ -16,7 +16,7 @@
package im.vector.matrix.android.api.session.crypto.sas
interface IncomingSasVerificationTransaction {
interface IncomingSasVerificationTransaction : SasVerificationTransaction {
val uxState: UxState
fun performAccept()

View File

@@ -16,7 +16,7 @@
package im.vector.matrix.android.api.session.crypto.sas
interface OutgoingSasVerificationRequest {
interface OutgoingSasVerificationTransaction : SasVerificationTransaction {
val uxState: UxState
enum class UxState {

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
interface QrCodeVerificationTransaction : VerificationTransaction {
/**
* To use to display a qr code, for the other user to scan it
*/
val qrCodeText: String?
/**
* Call when you have scan the other user QR code
*/
fun userHasScannedOtherQrCode(otherQrCodeText: String)
/**
* Call when you confirm that other user has scanned your QR code
*/
fun otherUserScannedMyQrCode()
/**
* Call when you do not confirm that other user has scanned your QR code
*/
fun otherUserDidNotScannedMyQrCode()
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
interface SasVerificationService {
fun addListener(listener: SasVerificationListener)
fun removeListener(listener: SasVerificationListener)
fun markedLocallyAsManuallyVerified(userId: String, deviceID: String)
fun getExistingTransaction(otherUser: String, tid: String): SasVerificationTransaction?
fun beginKeyVerificationSAS(userId: String, deviceID: String): String?
fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
// fun transactionUpdated(tx: SasVerificationTransaction)
interface SasVerificationListener {
fun transactionCreated(tx: SasVerificationTransaction)
fun transactionUpdated(tx: SasVerificationTransaction)
fun markedAsManuallyVerified(userId: String, deviceId: String)
}
}

View File

@@ -16,18 +16,7 @@
package im.vector.matrix.android.api.session.crypto.sas
interface SasVerificationTransaction {
val state: SasVerificationTxState
val cancelledReason: CancelCode?
val transactionId: String
val otherUserId: String
var otherDeviceId: String?
val isIncoming: Boolean
interface SasVerificationTransaction : VerificationTransaction {
fun supportsEmoji(): Boolean
@@ -37,14 +26,11 @@ interface SasVerificationTransaction {
fun getDecimalCodeRepresentation(): String
/**
* User wants to cancel the transaction
*/
fun cancel()
/**
* To be called by the client when the user has verified that
* both short codes do match
*/
fun userHasVerifiedShortCode()
fun shortCodeDoesNotMatch()
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
enum class SasVerificationTxState {
None,
// I have started a verification request
SendingStart,
Started,
// Other user/device sent me a request
OnStarted,
// I have accepted a request started by the other user/device
SendingAccept,
Accepted,
// My request has been accepted by the other user/device
OnAccepted,
// I have sent my public key
SendingKey,
KeySent,
// The other user/device has sent me his public key
OnKeyReceived,
// Short code is ready to be displayed
ShortCodeReady,
// I have compared the code and manually said that they match
ShortCodeAccepted,
SendingMac,
MacSent,
Verifying,
Verified,
// Global: The verification has been cancelled (by me or other), see cancelReason for details
Cancelled,
OnCancelled
}

View File

@@ -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.crypto.sas
/**
* Verification methods
*/
enum class VerificationMethod {
// Use it when your application supports the SAS verification method
SAS,
// Use it if your application is able to display QR codes
QR_CODE_SHOW,
// Use it if your application is able to scan QR codes
QR_CODE_SCAN
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
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
*
* Verifying keys manually by reading out the Ed25519 key is not very user friendly, and can lead to errors.
* Verification is a user-friendly key verification process.
* Verification is intended to be a highly interactive process for users,
* and as such exposes verification methods which are easier for users to use.
*/
interface VerificationService {
fun addListener(listener: VerificationListener)
fun removeListener(listener: VerificationListener)
/**
* Mark this device as verified manually
*/
fun markedLocallyAsManuallyVerified(userId: String, deviceID: String)
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>?
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest?
fun beginKeyVerification(method: VerificationMethod,
otherUserId: String,
otherDeviceId: String,
transactionId: String?): String?
/**
* Request a key verification from another user using toDevice events.
*/
fun requestKeyVerificationInDMs(methods: List<VerificationMethod>,
otherUserId: String,
roomId: String,
localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest
/**
* Request a key verification from another user using toDevice events.
*/
fun requestKeyVerification(methods: List<VerificationMethod>,
otherUserId: String,
otherDevices: List<String>?): PendingVerificationRequest
fun declineVerificationRequestInDMs(otherUserId: String,
otherDeviceId: String,
transactionId: String,
roomId: String)
// Only SAS method is supported for the moment
fun beginKeyVerificationInDMs(method: VerificationMethod,
transactionId: String,
roomId: String,
otherUserId: String,
otherDeviceId: String,
callback: MatrixCallback<String>?): String?
/**
* Returns false if the request is unknown
*/
fun readyPendingVerificationInDMs(methods: List<VerificationMethod>,
otherUserId: String,
roomId: String,
transactionId: String): Boolean
/**
* Returns false if the request is unknown
*/
fun readyPendingVerification(methods: List<VerificationMethod>,
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) {}
fun verificationRequestCreated(pr: PendingVerificationRequest) {}
fun verificationRequestUpdated(pr: PendingVerificationRequest) {}
}
companion object {
private const val TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000
private const val FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
fun isValidRequest(age: Long?): Boolean {
if (age == null) return false
val now = System.currentTimeMillis()
val tooInThePast = now - TEN_MINUTES_IN_MILLIS
val tooInTheFuture = now + FIVE_MINUTES_IN_MILLIS
return age in tooInThePast..tooInTheFuture
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
interface VerificationTransaction {
var state: VerificationTxState
val transactionId: String
val otherUserId: String
var otherDeviceId: String?
// TODO Not used. Remove?
val isIncoming: Boolean
/**
* User wants to cancel the transaction
*/
fun cancel()
fun cancel(code: CancelCode)
fun isToDeviceTransport(): Boolean
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.crypto.sas
sealed class VerificationTxState {
// Uninitialized state
object None : VerificationTxState()
// Specific for SAS
abstract class VerificationSasTxState : VerificationTxState()
object SendingStart : VerificationSasTxState()
object Started : VerificationSasTxState()
object OnStarted : VerificationSasTxState()
object SendingAccept : VerificationSasTxState()
object Accepted : VerificationSasTxState()
object OnAccepted : VerificationSasTxState()
object SendingKey : VerificationSasTxState()
object KeySent : VerificationSasTxState()
object OnKeyReceived : VerificationSasTxState()
object ShortCodeReady : VerificationSasTxState()
object ShortCodeAccepted : VerificationSasTxState()
object SendingMac : VerificationSasTxState()
object MacSent : VerificationSasTxState()
object Verifying : VerificationSasTxState()
// Specific for QR code
abstract class VerificationQrTxState : VerificationTxState()
// Will be used to ask the user if the other user has correctly scanned
object QrScannedByOther : VerificationQrTxState()
// Terminal states
abstract class TerminalTxState : VerificationTxState()
object Verified : TerminalTxState()
// Cancelled by me or by other
data class Cancelled(val cancelCode: CancelCode, val byMe: Boolean) : TerminalTxState()
}

View File

@@ -85,6 +85,14 @@ data class Event(
@Transient
var sendState: SendState = SendState.UNKNOWN
/**
* The `age` value transcoded in a timestamp based on the device clock when the SDK received
* the event from the home server.
* Unlike `age`, this value is static.
*/
@Transient
var ageLocalTs: Long? = null
/**
* Check if event is a state event.
* @return true if event is state event.

View File

@@ -25,7 +25,6 @@ object EventType {
const val MESSAGE = "m.room.message"
const val STICKER = "m.sticker"
const val ENCRYPTED = "m.room.encrypted"
const val ENCRYPTION = "m.room.encryption"
const val FEEDBACK = "m.room.message.feedback"
const val TYPING = "m.typing"
const val REDACTION = "m.room.redaction"
@@ -50,10 +49,11 @@ object EventType {
const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels"
const val STATE_ROOM_ALIASES = "m.room.aliases"
const val STATE_ROOM_TOMBSTONE = "m.room.tombstone"
const val STATE_CANONICAL_ALIAS = "m.room.canonical_alias"
const val STATE_HISTORY_VISIBILITY = "m.room.history_visibility"
const val STATE_RELATED_GROUPS = "m.room.related_groups"
const val STATE_PINNED_EVENT = "m.room.pinned_events"
const val STATE_ROOM_CANONICAL_ALIAS = "m.room.canonical_alias"
const val STATE_ROOM_HISTORY_VISIBILITY = "m.room.history_visibility"
const val STATE_ROOM_RELATED_GROUPS = "m.room.related_groups"
const val STATE_ROOM_PINNED_EVENT = "m.room.pinned_events"
const val STATE_ROOM_ENCRYPTION = "m.room.encryption"
// Call Events
@@ -72,6 +72,8 @@ object EventType {
const val KEY_VERIFICATION_KEY = "m.key.verification.key"
const val KEY_VERIFICATION_MAC = "m.key.verification.mac"
const val KEY_VERIFICATION_CANCEL = "m.key.verification.cancel"
const val KEY_VERIFICATION_DONE = "m.key.verification.done"
const val KEY_VERIFICATION_READY = "m.key.verification.ready"
// Relation Events
const val REACTION = "m.reaction"
@@ -86,10 +88,13 @@ object EventType {
STATE_ROOM_JOIN_RULES,
STATE_ROOM_GUEST_ACCESS,
STATE_ROOM_POWER_LEVELS,
STATE_ROOM_ALIASES,
STATE_ROOM_TOMBSTONE,
STATE_HISTORY_VISIBILITY,
STATE_RELATED_GROUPS,
STATE_PINNED_EVENT
STATE_ROOM_CANONICAL_ALIAS,
STATE_ROOM_HISTORY_VISIBILITY,
STATE_ROOM_RELATED_GROUPS,
STATE_ROOM_PINNED_EVENT,
STATE_ROOM_ENCRYPTION
)
fun isStateEvent(type: String): Boolean {

View File

@@ -21,9 +21,23 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class UnsignedData(
/**
* The time in milliseconds that has elapsed since the event was sent.
* This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers
* is out of sync, which can cause the age to either be negative or greater than it actually is.
*/
@Json(name = "age") val age: Long?,
/**
* Optional. The event that redacted this event, if any.
*/
@Json(name = "redacted_because") val redactedEvent: Event? = null,
/**
* The client-supplied transaction ID, if the client being given the event is the same one which sent it.
*/
@Json(name = "transaction_id") val transactionId: String? = null,
/**
* Optional. The previous content for this event. If there is no previous content, this key will be missing.
*/
@Json(name = "prev_content") val prevContent: Map<String, Any>? = null,
@Json(name = "m.relations") val relations: AggregatedRelations? = null
)

View File

@@ -31,9 +31,22 @@ interface GroupService {
*/
fun getGroup(groupId: String): Group?
/**
* Get a groupSummary from a groupId
* @param groupId the groupId to look for.
* @return the groupSummary with groupId or null
*/
fun getGroupSummary(groupId: String): GroupSummary?
/**
* Get a list of group summaries. This list is a snapshot of the data.
* @return the list of [GroupSummary]
*/
fun getGroupSummaries(groupSummaryQueryParams: GroupSummaryQueryParams): List<GroupSummary>
/**
* Get a live list of group summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of [GroupSummary]
*/
fun liveGroupSummaries(): LiveData<List<GroupSummary>>
fun getGroupSummariesLive(groupSummaryQueryParams: GroupSummaryQueryParams): LiveData<List<GroupSummary>>
}

View File

@@ -0,0 +1,44 @@
/*
* 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.group
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.room.model.Membership
fun groupSummaryQueryParams(init: (GroupSummaryQueryParams.Builder.() -> Unit) = {}): GroupSummaryQueryParams {
return GroupSummaryQueryParams.Builder().apply(init).build()
}
/**
* This class can be used to filter group summaries
*/
data class GroupSummaryQueryParams(
val displayName: QueryStringValue,
val memberships: List<Membership>
) {
class Builder {
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
var memberships: List<Membership> = Membership.all()
fun build() = GroupSummaryQueryParams(
displayName = displayName,
memberships = memberships
)
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.profile
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional
/**
* This interface defines methods to handling profile information. It's implemented at the session level.
*/
interface ProfileService {
companion object Constants {
const val DISPLAY_NAME_KEY = "displayname"
const val AVATAR_URL_KEY = "avatar_url"
}
/**
* Return the current dispayname for this user
* @param userId the userId param to look for
*
*/
fun getDisplayName(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable
/**
* Return the current avatarUrl for this user.
* @param userId the userId param to look for
*
*/
fun getAvatarUrl(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable
/**
* Get the combined profile information for this user.
* This may return keys which are not limited to displayname or avatar_url.
* @param userId the userId param to look for
*
*/
fun getProfile(userId: String, matrixCallback: MatrixCallback<JsonDict>): Cancelable
}

View File

@@ -58,7 +58,7 @@ interface PushersService {
const val EVENT_ID_ONLY = "event_id_only"
}
fun livePushers(): LiveData<List<Pusher>>
fun getPushersLive(): LiveData<List<Pusher>>
fun pushers() : List<Pusher>
}

View File

@@ -22,12 +22,13 @@ import im.vector.matrix.android.api.session.room.members.MembershipService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.relation.RelationService
import im.vector.matrix.android.api.session.room.notification.RoomPushRuleService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.reporting.ReportingService
import im.vector.matrix.android.api.session.room.send.DraftService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.state.StateService
import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.session.room.typing.TypingService
import im.vector.matrix.android.api.util.Optional
/**
@@ -38,6 +39,7 @@ interface Room :
SendService,
DraftService,
ReadService,
TypingService,
MembershipService,
StateService,
ReportingService,
@@ -56,5 +58,8 @@ interface Room :
*/
fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>>
/**
* A current snapshot of [RoomSummary] associated with the room
*/
fun roomSummary(): RoomSummary?
}

View File

@@ -21,6 +21,7 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.Optional
/**
* This interface defines methods to get rooms. It's implemented at the session level.
@@ -52,16 +53,35 @@ interface RoomService {
fun getRoom(roomId: String): Room?
/**
* Get a live list of room summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of [RoomSummary]
* Get a roomSummary from a roomId or a room alias
* @param roomIdOrAlias the roomId or the alias of a room to look for.
* @return a matching room summary or null
*/
fun liveRoomSummaries(): LiveData<List<RoomSummary>>
fun getRoomSummary(roomIdOrAlias: String): RoomSummary?
/**
* Get a snapshot list of room summaries.
* @return the immutable list of [RoomSummary]
*/
fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List<RoomSummary>
/**
* Get a live list of room summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of List[RoomSummary]
*/
fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<List<RoomSummary>>
/**
* Get a snapshot list of Breadcrumbs
* @return the immutable list of [RoomSummary]
*/
fun getBreadcrumbs(): List<RoomSummary>
/**
* Get a live list of Breadcrumbs
* @return the [LiveData] of [RoomSummary]
*/
fun liveBreadcrumbs(): LiveData<List<RoomSummary>>
fun getBreadcrumbsLive(): LiveData<List<RoomSummary>>
/**
* Inform the Matrix SDK that a room is displayed.
@@ -74,4 +94,13 @@ interface RoomService {
*/
fun markAllAsRead(roomIds: List<String>,
callback: MatrixCallback<Unit>): Cancelable
/**
* Resolve a room alias to a room ID.
*/
fun getRoomIdByAlias(roomAlias: String,
searchOnServer: Boolean,
callback: MatrixCallback<Optional<String>>): Cancelable
fun getExistingDirectRoomWithUser(otherUserId: String) : Room?
}

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
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.room.model.Membership
fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {}): RoomSummaryQueryParams {
return RoomSummaryQueryParams.Builder().apply(init).build()
}
/**
* This class can be used to filter room summaries to use with:
* [im.vector.matrix.android.api.session.room.Room] and [im.vector.matrix.android.api.session.room.RoomService]
*/
data class RoomSummaryQueryParams(
val displayName: QueryStringValue,
val canonicalAlias: QueryStringValue,
val memberships: List<Membership>
) {
class Builder {
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
var memberships: List<Membership> = Membership.all()
fun build() = RoomSummaryQueryParams(
displayName = displayName,
canonicalAlias = canonicalAlias,
memberships = memberships
)
}
}

View File

@@ -16,6 +16,8 @@
package im.vector.matrix.android.api.session.room.crypto
import im.vector.matrix.android.api.MatrixCallback
interface RoomCryptoService {
fun isEncrypted(): Boolean
@@ -23,4 +25,6 @@ interface RoomCryptoService {
fun encryptionAlgorithm(): String?
fun shouldEncryptForInvitedMembers(): Boolean
fun enableEncryptionWithAlgorithm(algorithm: String, callback: MatrixCallback<Unit>)
}

View File

@@ -18,7 +18,7 @@ package im.vector.matrix.android.api.session.room.members
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.util.Cancelable
/**
@@ -38,14 +38,21 @@ interface MembershipService {
*
* @return the roomMember with userId or null
*/
fun getRoomMember(userId: String): RoomMember?
fun getRoomMember(userId: String): RoomMemberSummary?
/**
* Return all the roomMembers ids of the room
*
* Return all the roomMembers of the room with params
* @param queryParams the params to query for
* @return a roomMember list.
*/
fun getRoomMembers(queryParams: RoomMemberQueryParams): List<RoomMemberSummary>
/**
* Return all the roomMembers of the room filtered by memberships
* @param queryParams the params to query for
* @return a [LiveData] of roomMember list.
*/
fun getRoomMemberIdsLive(): LiveData<List<String>>
fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData<List<RoomMemberSummary>>
fun getNumberOfJoinedMembers(): Int
@@ -59,7 +66,6 @@ interface MembershipService {
/**
* Join the room, or accept an invitation.
*/
fun join(reason: String? = null,
viaServers: List<String> = emptyList(),
callback: MatrixCallback<Unit>): Cancelable

View File

@@ -0,0 +1,50 @@
/*
* 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.members
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.room.model.Membership
fun roomMemberQueryParams(init: (RoomMemberQueryParams.Builder.() -> Unit) = {}): RoomMemberQueryParams {
return RoomMemberQueryParams.Builder().apply(init).build()
}
/**
* This class can be used to filter room members
*/
data class RoomMemberQueryParams(
val displayName: QueryStringValue,
val memberships: List<Membership>,
val userId: QueryStringValue,
val excludeSelf: Boolean
) {
class Builder {
var userId: QueryStringValue = QueryStringValue.NoCondition
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
var memberships: List<Membership> = Membership.all()
var excludeSelf: Boolean = false
fun build() = RoomMemberQueryParams(
displayName = displayName,
memberships = memberships,
userId = userId,
excludeSelf = excludeSelf
)
}
}

View File

@@ -18,5 +18,6 @@ package im.vector.matrix.android.api.session.room.model
data class EventAnnotationsSummary(
var eventId: String,
var reactionsSummary: List<ReactionAggregatedSummary>,
var editSummary: EditAggregatedSummary?
var editSummary: EditAggregatedSummary?,
var referencesAggregatedSummary: ReferencesAggregatedSummary? = null
)

View File

@@ -43,4 +43,14 @@ enum class Membership(val value: String) {
fun isLeft(): Boolean {
return this == KNOCK || this == LEAVE || this == BAN
}
companion object {
fun activeMemberships(): List<Membership> {
return listOf(INVITE, JOIN)
}
fun all(): List<Membership> {
return values().asList()
}
}
}

View File

@@ -1,121 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.events.model.EventType
/**
* Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content.
*/
@JsonClass(generateAdapter = true)
data class PowerLevels(
@Json(name = "ban") val ban: Int = 50,
@Json(name = "kick") val kick: Int = 50,
@Json(name = "invite") val invite: Int = 50,
@Json(name = "redact") val redact: Int = 50,
@Json(name = "events_default") val eventsDefault: Int = 0,
@Json(name = "events") val events: MutableMap<String, Int> = HashMap(),
@Json(name = "users_default") val usersDefault: Int = 0,
@Json(name = "users") val users: MutableMap<String, Int> = HashMap(),
@Json(name = "state_default") val stateDefault: Int = 50,
@Json(name = "notifications") val notifications: Map<String, Any> = HashMap()
) {
/**
* Returns the user power level of a dedicated user Id
*
* @param userId the user id
* @return the power level
*/
fun getUserPowerLevel(userId: String): Int {
return users.getOrElse(userId) { usersDefault }
}
/**
* Updates the user power levels of a dedicated user id
*
* @param userId the user
* @param powerLevel the new power level
*/
fun setUserPowerLevel(userId: String, powerLevel: Int) {
users[userId] = powerLevel
}
/**
* Tell if an user can send an event of type 'eventTypeString'.
*
* @param eventTypeString the event type (in Event.EVENT_TYPE_XXX values)
* @param userId the user id
* @return true if the user can send the event
*/
fun maySendEventOfType(eventTypeString: String, userId: String): Boolean {
return if (eventTypeString.isNotEmpty() && userId.isNotEmpty()) {
getUserPowerLevel(userId) >= minimumPowerLevelForSendingEventAsMessage(eventTypeString)
} else false
}
/**
* Tells if an user can send a room message.
*
* @param userId the user id
* @return true if the user can send a room message
*/
fun maySendMessage(userId: String): Boolean {
return maySendEventOfType(EventType.MESSAGE, userId)
}
/**
* Helper to get the minimum power level the user must have to send an event of the given type
* as a message.
*
* @param eventTypeString the type of event (in Event.EVENT_TYPE_XXX values)
* @return the required minimum power level.
*/
fun minimumPowerLevelForSendingEventAsMessage(eventTypeString: String?): Int {
return events[eventTypeString] ?: eventsDefault
}
/**
* Helper to get the minimum power level the user must have to send an event of the given type
* as a state event.
*
* @param eventTypeString the type of event (in Event.EVENT_TYPE_STATE_ values).
* @return the required minimum power level.
*/
fun minimumPowerLevelForSendingEventAsStateEvent(eventTypeString: String?): Int {
return events[eventTypeString] ?: stateDefault
}
/**
* Get the notification level for a dedicated key.
*
* @param key the notification key
* @return the level
*/
fun notificationLevel(key: String): Int {
val valAsVoid = notifications[key] ?: return 50
// the first implementation was a string value
return if (valAsVoid is String) {
valAsVoid.toInt()
} else {
valAsVoid as Int
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants
/**
* Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content.
*/
@JsonClass(generateAdapter = true)
data class PowerLevelsContent(
@Json(name = "ban") val ban: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "kick") val kick: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "invite") val invite: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "redact") val redact: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "events_default") val eventsDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL,
@Json(name = "events") val events: MutableMap<String, Int> = HashMap(),
@Json(name = "users_default") val usersDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL,
@Json(name = "users") val users: MutableMap<String, Int> = HashMap(),
@Json(name = "state_default") val stateDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "notifications") val notifications: Map<String, Any> = HashMap()
)

View File

@@ -0,0 +1,31 @@
/*
* 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.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Contains an aggregated summary info of the references.
* Put pre-computed info that you want to access quickly without having
* to go through all references events
*/
@JsonClass(generateAdapter = true)
data class ReferencesAggregatedContent(
// Verification status info for m.key.verification.request msgType events
@Json(name = "verif_sum") val verificationSummary: String
// Add more fields for future summary info.
)

View File

@@ -0,0 +1,29 @@
/*
* 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.room.model
import im.vector.matrix.android.api.session.events.model.Content
/**
* Events can relates to other events, this object keeps a summary
* of all events that are referencing the 'eventId' event via the RelationType.REFERENCE
*/
data class ReferencesAggregatedSummary(
val eventId: String,
val content: Content?,
val sourceEvents: List<String>,
val localEchos: List<String>
)

View File

@@ -20,7 +20,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Class representing the EventType.STATE_CANONICAL_ALIAS state event content
* Class representing the EventType.STATE_ROOM_CANONICAL_ALIAS state event content
*/
@JsonClass(generateAdapter = true)
data class RoomCanonicalAliasContent(

View File

@@ -0,0 +1,37 @@
/*
* 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.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Class representing the EventType.STATE_ROOM_GUEST_ACCESS state event content
* Ref: https://matrix.org/docs/spec/client_server/latest#m-room-guest-access
*/
@JsonClass(generateAdapter = true)
data class RoomGuestAccessContent(
// Required. Whether guests can join the room. One of: ["can_join", "forbidden"]
@Json(name = "guest_access") val guestAccess: GuestAccess? = null
)
enum class GuestAccess(val value: String) {
@Json(name = "can_join")
CanJoin("can_join"),
@Json(name = "forbidden")
Forbidden("forbidden")
}

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