mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-08 09:12:49 +02:00
Compare commits
951 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
902a9aa243 | ||
|
d91ff87fb9 | ||
|
79ef055bfb | ||
|
3a761be6b4 | ||
|
a9e2c31c32 | ||
|
3ac53d20e9 | ||
|
3c18fd5335 | ||
|
f00f34b244 | ||
|
63e0b15f3d | ||
|
80306f20df | ||
|
2972177541 | ||
|
1ad8f47dc1 | ||
|
8527d3f162 | ||
|
99423bacb2 | ||
|
edc6c3dd4f | ||
|
a761a0dbd2 | ||
|
d431ab23c8 | ||
|
f0aa34774e | ||
|
742136abe8 | ||
|
36aba8554d | ||
|
da14ae432a | ||
|
9a01b4ace9 | ||
|
109c1fe482 | ||
|
dbd4525404 | ||
|
c714266a81 | ||
|
8b1701e537 | ||
|
41d1b77370 | ||
|
ac75fe12bf | ||
|
2f26f4b8bb | ||
|
6d82ac7c59 | ||
|
411afb0bf3 | ||
|
57354cbd69 | ||
|
03d51281a2 | ||
|
415511f3e0 | ||
|
e0e778909d | ||
|
b9efc9f4bd | ||
|
872b14373b | ||
|
d28700e2bf | ||
|
18beef14cf | ||
|
e73923dca3 | ||
|
94afd3e66d | ||
|
5f540a5b45 | ||
|
a41617e8aa | ||
|
ff1745b5dc | ||
|
8e3e9876b8 | ||
|
9a4d8f87f6 | ||
|
aef76241a3 | ||
|
0768bd5c88 | ||
|
65333e6031 | ||
|
849e7c613c | ||
|
60169d53d7 | ||
|
4234c27af9 | ||
|
f9c0256afd | ||
|
b9eb85e0a6 | ||
|
6d8850b3d6 | ||
|
d88edd578f | ||
|
5373d9aa21 | ||
|
ad4d5e5c02 | ||
|
eb9775e307 | ||
|
aa9d66b991 | ||
|
4ff12605e9 | ||
|
7c561ae622 | ||
|
cec08a20e5 | ||
|
fb8ba32fb4 | ||
|
8e9ac8198d | ||
|
7a05207ae4 | ||
|
6b39cf3b70 | ||
|
994759e11a | ||
|
f31c1b69cb | ||
|
bdb9d2fbb8 | ||
|
9fb50dde32 | ||
|
a145aae0aa | ||
|
3623072f08 | ||
|
2717ad475a | ||
|
a6f8fe9317 | ||
|
f9eb80b4ec | ||
|
9510d71cd3 | ||
|
e7a47ae32a | ||
|
7890b929a7 | ||
|
0376de08f4 | ||
|
90c472fef9 | ||
|
8e873672a9 | ||
|
bba52e77d1 | ||
|
64d73ae8e6 | ||
|
d9982076f9 | ||
|
ab489df83d | ||
|
5e07e96bdb | ||
|
c495aa4914 | ||
|
ff267ba9bc | ||
|
69f923383c | ||
|
c69852c849 | ||
|
6d7f2670df | ||
|
71de8fdad3 | ||
|
998d9f2c59 | ||
|
4f3da353e4 | ||
|
4154cb2b85 | ||
|
3c6eb4bccf | ||
|
7b4398404b | ||
|
9b882978ed | ||
|
49178dc633 | ||
|
490ce4b51d | ||
|
5b63856d96 | ||
|
538c4d1a64 | ||
|
1cadbb8eed | ||
|
3f4f7457c7 | ||
|
ebf21fe9d8 | ||
|
a343da594f | ||
|
938289e8eb | ||
|
e23763e6db | ||
|
c06b8486ea | ||
|
67fe776d91 | ||
|
10cc270273 | ||
|
46d96429e0 | ||
|
9f9c418085 | ||
|
c412006f0e | ||
|
5d3c376267 | ||
|
a3f8f138a6 | ||
|
4b273e8746 | ||
|
f11cd47df3 | ||
|
f984758d37 | ||
|
97766404d6 | ||
|
38b93c527b | ||
|
62bae67080 | ||
|
2a4cdec020 | ||
|
6bd7257cf2 | ||
|
500eb113b6 | ||
|
1bec8c29b8 | ||
|
0ecb23199c | ||
|
33925fcf57 | ||
|
bf9ce4f690 | ||
|
d2a4163dff | ||
|
a0d7aef92e | ||
|
29f32cf8eb | ||
|
bb1c988a49 | ||
|
f063abe068 | ||
|
db87d8f644 | ||
|
efa858a337 | ||
|
fd90f3b9fc | ||
|
aa51764068 | ||
|
0a19ded167 | ||
|
2e3763e8b4 | ||
|
0c4e0890b1 | ||
|
e532d97ec1 | ||
|
fbde8d7d18 | ||
|
f96bea742e | ||
|
86bfdd011e | ||
|
d5c2c1938c | ||
|
7ce8a13ddf | ||
|
9bd4dbb65f | ||
|
ee875b359b | ||
|
3eb2e1655f | ||
|
9b207dd5dc | ||
|
3f1540b54e | ||
|
2b30925163 | ||
|
4690754f5f | ||
|
a9526cdd92 | ||
|
ab4d42fb20 | ||
|
0014e8ef06 | ||
|
311d8ddf7b | ||
|
6cb3c222a9 | ||
|
f84ec08847 | ||
|
9d0188cbf1 | ||
|
73462a3045 | ||
|
3eebf965e5 | ||
|
bba58d25e1 | ||
|
fedb45b019 | ||
|
9b83f08654 | ||
|
91fcf428dd | ||
|
7e1a279fd9 | ||
|
8de1fa835b | ||
|
af45c554fd | ||
|
11bc7051fd | ||
|
489a594027 | ||
|
3f83c161e4 | ||
|
e0a36b794f | ||
|
d2b516bdc2 | ||
|
37166caea2 | ||
|
9fa131c297 | ||
|
71ae3c4a8c | ||
|
f87526e615 | ||
|
51f53e2ae9 | ||
|
ef35f0a044 | ||
|
5db3f51ddb | ||
|
49f7ce3554 | ||
|
a3111dc2d8 | ||
|
be95542110 | ||
|
6723a566c2 | ||
|
90027cc4d5 | ||
|
810b226f21 | ||
|
42c5adf08d | ||
|
5edfb78721 | ||
|
491a38a79f | ||
|
051f77087e | ||
|
1a603742d0 | ||
|
edb65f1787 | ||
|
9af8355c07 | ||
|
dd44078297 | ||
|
a1f96a5b5a | ||
|
5770023593 | ||
|
2789268c23 | ||
|
eb4355890e | ||
|
4e41156db3 | ||
|
1a0b8b35f8 | ||
|
5f9cdcb4b4 | ||
|
2e4c3f850a | ||
|
127916a8d9 | ||
|
248a584e1a | ||
|
b8a3ad0c43 | ||
|
1f161b7e23 | ||
|
23315ede92 | ||
|
20ad3abb60 | ||
|
ac377fceba | ||
|
abbe56acfa | ||
|
f74cabd145 | ||
|
0e2237226f | ||
|
62d5aba796 | ||
|
f12e6c941d | ||
|
7caa8ce3bc | ||
|
20f969d563 | ||
|
a8f24e5c39 | ||
|
8ae9544b48 | ||
|
3758334824 | ||
|
6d8e5b892e | ||
|
c18c140ec9 | ||
|
1dc7dfc896 | ||
|
1c03163a33 | ||
|
9aa270c7ad | ||
|
3f80076fb1 | ||
|
dfbf448bb7 | ||
|
95fc20dca0 | ||
|
381084b2ab | ||
|
41ac2c6d70 | ||
|
08ea3d049e | ||
|
f24889230c | ||
|
b5f9549a8b | ||
|
e3e38d4c8a | ||
|
416bef7903 | ||
|
823acebf78 | ||
|
3e91125872 | ||
|
9a628c7b5d | ||
|
fb46a14172 | ||
|
ca4e75a1a0 | ||
|
2871e4f5b1 | ||
|
b7bfb20a2e | ||
|
a1aa16715d | ||
|
55add4734d | ||
|
2849e1f846 | ||
|
5b9876a20c | ||
|
adf299081d | ||
|
d50b690523 | ||
|
c6b0ae63ea | ||
|
3c93807fe6 | ||
|
7f1f98c2e5 | ||
|
6525314af8 | ||
|
da8d6fb4f4 | ||
|
fa6a9cab7e | ||
|
bdfc4ad8a7 | ||
|
6ab7209e4d | ||
|
4485d1c685 | ||
|
8b63f78d76 | ||
|
2e87e0b4c1 | ||
|
507134407b | ||
|
7663cd4e23 | ||
|
ec2954200e | ||
|
eb32c5455f | ||
|
fc367b3c3e | ||
|
57dcd569f3 | ||
|
3673520ef6 | ||
|
fe17050580 | ||
|
ec40a8c969 | ||
|
6b1b3bec85 | ||
|
6bd6ececb7 | ||
|
c7db695e67 | ||
|
4cefdfedce | ||
|
6ce241163e | ||
|
79350899c5 | ||
|
f265724a3c | ||
|
2e50d2a36e | ||
|
643c062858 | ||
|
0e0db67aef | ||
|
6dc5b126d6 | ||
|
d2acabddd9 | ||
|
ec71b53c1e | ||
|
fc3d4187d1 | ||
|
a25f309990 | ||
|
5449592422 | ||
|
19b415871d | ||
|
6463f3439f | ||
|
f2320f9571 | ||
|
fc91694bdd | ||
|
dbb41108ef | ||
|
08c864bad7 | ||
|
9c5c65a243 | ||
|
b6199b1f27 | ||
|
38da54119a | ||
|
65b09ad4f0 | ||
|
603b8fae45 | ||
|
50e2e6a823 | ||
|
bb237e3bbb | ||
|
1bd2c0d220 | ||
|
bcb811a7e8 | ||
|
ec4d7e29ec | ||
|
a6df63f6d9 | ||
|
ea7213a5ae | ||
|
590a13334d | ||
|
631448335d | ||
|
12376368c7 | ||
|
f17564d743 | ||
|
a6fcc7dca6 | ||
|
70bce9e7dd | ||
|
17f3614288 | ||
|
238d1d87c6 | ||
|
82f639b91f | ||
|
c8bc553caa | ||
|
fa5d44af65 | ||
|
cbdbe5033f | ||
|
61ac250e2b | ||
|
04f72dfcb8 | ||
|
10ca5d94ea | ||
|
c5b8c69ae5 | ||
|
d3d7f7cc61 | ||
|
b6bb714264 | ||
|
a87310ac15 | ||
|
032e1b3d19 | ||
|
d9f15c1d21 | ||
|
99d09f71ad | ||
|
9c952b6bc8 | ||
|
fbae3d27c2 | ||
|
2f7d1f9f01 | ||
|
114101699d | ||
|
f5c0dcb5ea | ||
|
241220ce1f | ||
|
98d97e574c | ||
|
96e610970a | ||
|
2027802f82 | ||
|
54f93db632 | ||
|
3af7ca9ab0 | ||
|
93ef3edab3 | ||
|
c85852262e | ||
|
d0c3271628 | ||
|
ad9a48d5fa | ||
|
219d1383e5 | ||
|
8871280fab | ||
|
fb3e953e28 | ||
|
10712fd6ab | ||
|
9d478dbfe2 | ||
|
3013d67c16 | ||
|
bee8c2d159 | ||
|
945e5d5a74 | ||
|
93df8c56a8 | ||
|
cd1a964067 | ||
|
e4b829f0cf | ||
|
7206d84a6b | ||
|
b3233d3eb7 | ||
|
3c4c0ed46a | ||
|
24f1262005 | ||
|
86667a6d8a | ||
|
42e0d0f769 | ||
|
e976055253 | ||
|
84d6c8ec16 | ||
|
9fdfd091ac | ||
|
e66766f41c | ||
|
6177e69855 | ||
|
5c71cabb5f | ||
|
6ebe5532c5 | ||
|
8030c44f44 | ||
|
a85b5af761 | ||
|
d780c74abf | ||
|
5d7efa7f8f | ||
|
7e467443ed | ||
|
8439c337f7 | ||
|
151ad01038 | ||
|
73267442bb | ||
|
43fd794c96 | ||
|
36060fe332 | ||
|
3483debcc1 | ||
|
4324f6abbd | ||
|
43f8d8d8aa | ||
|
fb1ff77ec4 | ||
|
e355a7f6dd | ||
|
33e35368fc | ||
|
0e49a11e5e | ||
|
d47cf7e932 | ||
|
101057520b | ||
|
30b2e53002 | ||
|
5ab31a0ef5 | ||
|
b4ae331086 | ||
|
3f447df13c | ||
|
3517873156 | ||
|
118870bc41 | ||
|
d001ab5bef | ||
|
7496a88dcd | ||
|
6567c5e6c7 | ||
|
361427488f | ||
|
7272343e6d | ||
|
f0b3151d71 | ||
|
035359cb35 | ||
|
57b640622b | ||
|
de4c389c76 | ||
|
199456487c | ||
|
00ca5dc70a | ||
|
a04802b238 | ||
|
cb275aee37 | ||
|
fbf73c7c8e | ||
|
0040f8e924 | ||
|
6cca242f77 | ||
|
2929b8f617 | ||
|
8422c6de17 | ||
|
7c567b04bb | ||
|
1ac99e92a6 | ||
|
5ab975cc5c | ||
|
2cf63ea92a | ||
|
9e8d8ce878 | ||
|
b766bce07d | ||
|
01452efd8d | ||
|
0a0af221f0 | ||
|
af08759af6 | ||
|
e52f0faaa7 | ||
|
8fa676d034 | ||
|
b6594599c4 | ||
|
8be8cc9ef7 | ||
|
9762d5be40 | ||
|
b17b54d218 | ||
|
187e2a26db | ||
|
2f5fdbb7e2 | ||
|
8b1411f533 | ||
|
bdee5e0687 | ||
|
ce4e244a3b | ||
|
ff81715783 | ||
|
3196dcb57e | ||
|
50bf6df7fe | ||
|
02914495ce | ||
|
70a14f6350 | ||
|
cac5fb725a | ||
|
dbc17ae515 | ||
|
1de02c2fbb | ||
|
6d55c15761 | ||
|
377a228f88 | ||
|
2974f8b200 | ||
|
7388a408b8 | ||
|
f43dcb1183 | ||
|
492ed3954a | ||
|
7890e83204 | ||
|
00d1a2c380 | ||
|
78dfd6b3e6 | ||
|
3abce34484 | ||
|
4204ab262c | ||
|
c7a4d34192 | ||
|
7416fec93e | ||
|
3c40f64fb7 | ||
|
b57c71b1c9 | ||
|
fea54952d3 | ||
|
3dc5ef54ab | ||
|
9092b97fb8 | ||
|
cebd8136da | ||
|
64b3568d51 | ||
|
5e4e54153c | ||
|
d071324694 | ||
|
2c8cd89533 | ||
|
11b5c2c3ba | ||
|
9d7c4abb97 | ||
|
8e3234d188 | ||
|
b253722b98 | ||
|
fce576e3a4 | ||
|
7ed7b18ccd | ||
|
053bf7aeac | ||
|
6ccd083451 | ||
|
e39c4a7925 | ||
|
abdb83b9fd | ||
|
0bcc84cbd6 | ||
|
b2f6fb8c91 | ||
|
36042ed145 | ||
|
6ad1932fe5 | ||
|
4a6237b50e | ||
|
a7a19dab11 | ||
|
8d0aa0437c | ||
|
0a79b8b315 | ||
|
1dacfa6744 | ||
|
723a007c39 | ||
|
eaa1b04a4a | ||
|
b1710fde60 | ||
|
cd0a40c18d | ||
|
17636019e0 | ||
|
8078c39d6e | ||
|
be94b2f90a | ||
|
eff04be247 | ||
|
3986839801 | ||
|
9e436483de | ||
|
05a069be04 | ||
|
a1a71e2f1d | ||
|
203da0f37e | ||
|
6cd04525aa | ||
|
3c3c6aeac6 | ||
|
e71311f576 | ||
|
e4d0e0b0bf | ||
|
28e5e42ab1 | ||
|
b860c3b0e3 | ||
|
f7f97e2098 | ||
|
e28e2dadb9 | ||
|
c28be6adb0 | ||
|
c57af9cf3e | ||
|
679b0fff98 | ||
|
946fc36a26 | ||
|
13a5f784dc | ||
|
0ca8696e88 | ||
|
3622c0ecb4 | ||
|
116d569fa8 | ||
|
ee5ebb4b83 | ||
|
0a0c344bfb | ||
|
82fc97f619 | ||
|
20696353b8 | ||
|
ae5b6bd2b9 | ||
|
1e11d4492b | ||
|
6e39164b20 | ||
|
0a9ebb6bf6 | ||
|
db009ce683 | ||
|
55c80d3743 | ||
|
fbb23dfb66 | ||
|
e5779d425a | ||
|
99d9704a50 | ||
|
3f8ddbe880 | ||
|
30e43e47cd | ||
|
15dc4d6369 | ||
|
dceb5ffd8d | ||
|
eec470f2ce | ||
|
68db9c1cc0 | ||
|
cdfc402599 | ||
|
72d3f1e909 | ||
|
255fa11e89 | ||
|
119e4c0d32 | ||
|
a9c474105a | ||
|
6de64cbedd | ||
|
546c537e3b | ||
|
36c5f9af13 | ||
|
c2682c7f4b | ||
|
3073470c38 | ||
|
549f749682 | ||
|
d4dfb76e80 | ||
|
e80191b2e0 | ||
|
c62c77f14c | ||
|
d6e5c5a857 | ||
|
50a0660ab6 | ||
|
ecdb3c3326 | ||
|
2cd1d697fe | ||
|
3f9b7813bc | ||
|
f34f28b668 | ||
|
53572a3be6 | ||
|
90b6199e10 | ||
|
0aa299aa37 | ||
|
d387c310c8 | ||
|
8bd1fb08f7 | ||
|
ac6aff9175 | ||
|
adf0382d28 | ||
|
51554f7be0 | ||
|
c1c1c3f999 | ||
|
8b04fdab77 | ||
|
f8b665a245 | ||
|
d68a9a5342 | ||
|
5d2ff589f8 | ||
|
e85a0783fc | ||
|
d6c278288d | ||
|
4ad86a13a0 | ||
|
4f7ec91255 | ||
|
979b42aa30 | ||
|
fc49de080c | ||
|
d2b9668d4e | ||
|
0632870be1 | ||
|
8e39fd2a70 | ||
|
abbc62dd35 | ||
|
77de059dc9 | ||
|
1931a1a4a4 | ||
|
9c5987b682 | ||
|
4e4fb4c565 | ||
|
0582d0f641 | ||
|
ef2af14529 | ||
|
525da17678 | ||
|
aab41d7358 | ||
|
5db3c81aa9 | ||
|
c763635845 | ||
|
11d72b81f6 | ||
|
53543453b3 | ||
|
d4be68191c | ||
|
7ef471ad0d | ||
|
73dd735ba6 | ||
|
2f6d3adb17 | ||
|
2edfd4e830 | ||
|
ff7856c535 | ||
|
650a151b18 | ||
|
275dd20412 | ||
|
44f6391cb4 | ||
|
588e5d6e63 | ||
|
716999eec6 | ||
|
42e0a45f3f | ||
|
31397869b2 | ||
|
e842bf13b2 | ||
|
aea34da81e | ||
|
0814f53fed | ||
|
b5c6c1af0d | ||
|
de30e7c1c6 | ||
|
2d95fe921d | ||
|
84542326f4 | ||
|
53b1b89c47 | ||
|
28315be7b9 | ||
|
8605095668 | ||
|
737959f616 | ||
|
7817f49072 | ||
|
a060431aaf | ||
|
a3f561d788 | ||
|
0ea878af8a | ||
|
99de40c980 | ||
|
810a97c639 | ||
|
f02f16d9c5 | ||
|
62b7a83a31 | ||
|
4a80df082c | ||
|
60f6b3ef02 | ||
|
a0b1ef3216 | ||
|
1b66d1f746 | ||
|
643a2baabf | ||
|
cd62e87266 | ||
|
17cba1a432 | ||
|
f077cc8467 | ||
|
f3039601bf | ||
|
4c04014e4d | ||
|
ae8bceacba | ||
|
9b91b6ea87 | ||
|
b24a372262 | ||
|
63b43de4b8 | ||
|
d1a61f29e4 | ||
|
f6373221de | ||
|
ec0974f72c | ||
|
b5f2f01c8d | ||
|
21d808c1ce | ||
|
1e963bc0dc | ||
|
0d80750507 | ||
|
1c9cf7a810 | ||
|
c6d01fbcf4 | ||
|
9e1ded941f | ||
|
af433266c8 | ||
|
05d09bf950 | ||
|
6890f83810 | ||
|
51568c30a6 | ||
|
cc832633a5 | ||
|
e019ec6596 | ||
|
eadea9016b | ||
|
6422d946c9 | ||
|
5cc3dc00e3 | ||
|
5a2a9f908a | ||
|
c1f2e9f171 | ||
|
f6d34ec7fd | ||
|
620ba279d8 | ||
|
3fcfa33364 | ||
|
546da0f173 | ||
|
001711d5a3 | ||
|
8e1a964679 | ||
|
b25a130db1 | ||
|
8a9e6497e8 | ||
|
47e3797b7e | ||
|
5cbc90e06a | ||
|
b6e18e4a8f | ||
|
7e29665fd0 | ||
|
e04bf31faa | ||
|
d25cf79b07 | ||
|
faa8e6bbb2 | ||
|
d3d4deb884 | ||
|
f6b8e0c479 | ||
|
2a726f54a2 | ||
|
1197d4021d | ||
|
03f8120b7d | ||
|
acd7a709de | ||
|
5651ea515b | ||
|
9794b3a49d | ||
|
b3e1c3969d | ||
|
90eeb68d36 | ||
|
d1ff3314a7 | ||
|
f24bed17a2 | ||
|
a993a30203 | ||
|
ea0809ff87 | ||
|
9668487b6b | ||
|
91cc78d2ad | ||
|
562acc9702 | ||
|
dfab88ed95 | ||
|
36866dd24e | ||
|
c728834273 | ||
|
f5020d0f63 | ||
|
7da9cafcc2 | ||
|
6f09eea248 | ||
|
468bd5bcc9 | ||
|
3169093c50 | ||
|
d60d766354 | ||
|
0ffb5e627e | ||
|
b4a13f9504 | ||
|
88fb9667a3 | ||
|
ffa8b7e73a | ||
|
528958b3de | ||
|
3ffe2f7d40 | ||
|
3066d5f303 | ||
|
bf42b73713 | ||
|
ed93f4a6c1 | ||
|
b3d649a4d9 | ||
|
3739e50d46 | ||
|
9bf484cf1e | ||
|
6c2faff1f0 | ||
|
07fca0922b | ||
|
282de21708 | ||
|
ba9d119892 | ||
|
4453f0ced9 | ||
|
77168bfd6a | ||
|
25e9a179d2 | ||
|
73ec0f5a83 | ||
|
993fa74252 | ||
|
38fc4984fe | ||
|
695d8cce00 | ||
|
07e99901e1 | ||
|
20f53e9a58 | ||
|
ced72aff4f | ||
|
fdaaca49c2 | ||
|
3485f023b0 | ||
|
384dd100e9 | ||
|
1ba8a58219 | ||
|
69fb7bdf95 | ||
|
c8010561fc | ||
|
1f127335bc | ||
|
138a210a73 | ||
|
ca6bcde82d | ||
|
6bda437f5d | ||
|
5d6d0202a9 | ||
|
3e6b65e174 | ||
|
137dcab734 | ||
|
b22b8fba02 | ||
|
3ccdf4a244 | ||
|
5fbd271b1c | ||
|
db8ea0f5e8 | ||
|
a47a3ead1f | ||
|
05b2092ffc | ||
|
f4ab770be9 | ||
|
6249a59203 | ||
|
d4111d053d | ||
|
618e9a4f52 | ||
|
b8ebe3570b | ||
|
f2c8d4ad02 | ||
|
be524472ec | ||
|
1b82a1a24d | ||
|
cf0b331c3b | ||
|
2a92a3dc80 | ||
|
012840abba | ||
|
a5975a099e | ||
|
38da4b9ee5 | ||
|
242e60fcaa | ||
|
a23be05cbf | ||
|
ed39b02924 | ||
|
fe931b5361 | ||
|
90d9cd0587 | ||
|
9cedb18921 | ||
|
e89ba7b87b | ||
|
902657c22a | ||
|
eec2abf164 | ||
|
6879cc8ca8 | ||
|
fd6bbbd3b5 | ||
|
0ff0b014a9 | ||
|
a89f0ddd1d | ||
|
fdc9e84dd5 | ||
|
58f878fca9 | ||
|
88095e4bd9 | ||
|
47d22a3d5e | ||
|
28e82cb8ea | ||
|
35817245cb | ||
|
75266f42bb | ||
|
95c4c9ce56 | ||
|
ce5570105d | ||
|
188a9aebfa | ||
|
c95223f5d2 | ||
|
ef0362ba9c | ||
|
ea242f6737 | ||
|
cbc08d834b | ||
|
0ab6b33fb6 | ||
|
1b394527b6 | ||
|
a8f1388721 | ||
|
166be4e289 | ||
|
b49ccefe63 | ||
|
825760d17e | ||
|
b5af62c3ea | ||
|
a51d96bf00 | ||
|
7e142d201d | ||
|
2be6058971 | ||
|
49d73f360e | ||
|
bd88d85a21 | ||
|
be4fc5cce6 | ||
|
704da1be55 | ||
|
5d002532d3 | ||
|
d4161e9a1a | ||
|
7966ebef03 | ||
|
ed5faca5d2 | ||
|
51a4c93676 | ||
|
d8f449388c | ||
|
8ca829d538 | ||
|
e7819ce678 | ||
|
5402902bc2 | ||
|
bc1350aaf5 | ||
|
fd74e3dfb1 | ||
|
e0628da1cb | ||
|
aa4e74e986 | ||
|
6cc0c0672e | ||
|
501474b720 | ||
|
e11c66035c | ||
|
3d2d219d79 | ||
|
63af03bedd | ||
|
d3827b8673 | ||
|
4ca2531e47 | ||
|
4e8dc72439 | ||
|
25a4240a5a | ||
|
b9cfda23b6 | ||
|
06dcf75a32 | ||
|
21deb2551d | ||
|
70639f180c | ||
|
1dbb02a80d | ||
|
825463d9cd | ||
|
c313ce78cb | ||
|
39f58d048b | ||
|
3f792c7a84 | ||
|
347dcb469a | ||
|
9cd69d1e33 | ||
|
79fb1985aa | ||
|
e216cd15a8 | ||
|
37fde374b3 | ||
|
f7b471f141 | ||
|
93fd56a7ca | ||
|
5a9d88e791 | ||
|
eaf6a9923a | ||
|
d98567045c | ||
|
b4ce8748cb | ||
|
9d5433a857 | ||
|
6d4ee83e65 | ||
|
6e44cca17d | ||
|
0a73887c70 | ||
|
7fef063e15 | ||
|
24f391dac0 | ||
|
81c7f694d6 | ||
|
80e2fc0ca3 | ||
|
9f53406e99 | ||
|
3584658c36 | ||
|
12a0cbb400 | ||
|
20437446b4 | ||
|
35229882e3 | ||
|
a04f4421f6 | ||
|
af1e81f65e | ||
|
63f6081fa5 | ||
|
ee2e575211 | ||
|
0949d29f9c | ||
|
23466fb5a4 | ||
|
7f09e64d63 | ||
|
585f0ba4b7 | ||
|
245fbe86d9 | ||
|
456908c851 | ||
|
b79fdf6a85 | ||
|
d9f448c9aa | ||
|
7a6fc4936b | ||
|
d82fd10f3b | ||
|
4009f2c176 | ||
|
15c4b03340 | ||
|
7b5dff3dcf | ||
|
357123743f | ||
|
bb04af1e2c | ||
|
2f94fbd7eb | ||
|
f2a3bdb68e | ||
|
097e9714ff | ||
|
acae0fad3e | ||
|
4deb7eb865 | ||
|
f910cd6f97 | ||
|
652ac81fa1 | ||
|
99f4196388 | ||
|
c0b94f4111 | ||
|
1462fa0484 | ||
|
dafdc1d3ad | ||
|
394b89e76b | ||
|
0db8e7da43 | ||
|
dae8b5c196 | ||
|
215324a03e | ||
|
d3ce4c491c | ||
|
ed6d28bd3b | ||
|
c2e053b62b | ||
|
c450849cc3 | ||
|
fe884dba2d | ||
|
3fa4dbaa25 | ||
|
4a74f58516 | ||
|
c413321a22 | ||
|
d696bd2830 | ||
|
a2b6bd0f62 | ||
|
9cc922a8a2 | ||
|
c36d1bcd06 | ||
|
85499c6b33 | ||
|
8076eab4b5 | ||
|
d47c0f5ebc | ||
|
fd09a1224e | ||
|
c300c50093 | ||
|
77c4355aed | ||
|
1a92562182 | ||
|
9c390dcc0c | ||
|
95089b91b8 | ||
|
eb446d7b49 | ||
|
dc4786ecf0 | ||
|
e245023add | ||
|
90fad23493 | ||
|
000db4b192 | ||
|
2a16c36a59 | ||
|
ef6c1cfc63 | ||
|
4b4156996d | ||
|
087cc0e6e3 | ||
|
f4df27c2dc | ||
|
ab25980c4e | ||
|
77b402ce70 | ||
|
2763fbb496 | ||
|
6deba31111 | ||
|
ff6ce8a4b7 | ||
|
d0cff219aa | ||
|
65f0af918f | ||
|
ac38a6461c | ||
|
9a1e16a170 | ||
|
9e5c70dda3 | ||
|
0255696c88 | ||
|
76a9625f25 | ||
|
5af6bf3762 | ||
|
507bc2f622 | ||
|
125eacb20b | ||
|
6176520805 | ||
|
3aea0a50ca | ||
|
ab87a3caea | ||
|
c58328f94e | ||
|
03974c8bdf | ||
|
151ae7f4dd | ||
|
a34b053efe | ||
|
02e342849f | ||
|
b59017938b | ||
|
2c81e41288 | ||
|
cb44ab547c | ||
|
6d01a570fd | ||
|
4a2bf0d6c6 | ||
|
36af8a6a9f | ||
|
40a68c3e9f | ||
|
1a4ec34bb2 | ||
|
10490e3aa6 | ||
|
cd6624a8a6 | ||
|
3965218bf9 | ||
|
d78ff7ab08 | ||
|
cb274d6a33 | ||
|
c00dbce536 | ||
|
db88caf7fa | ||
|
c3d945d6bb | ||
|
4c128602b2 | ||
|
001603cf9a | ||
|
4341b0d0f5 | ||
|
838003b68a |
@@ -1,15 +1,38 @@
|
|||||||
# Use Docker file from https://hub.docker.com/r/runmymind/docker-android-sdk
|
# Use Docker file from https://hub.docker.com/r/runmymind/docker-android-sdk
|
||||||
# Last docker plugin version can be found here:
|
# Last docker plugin version can be found here:
|
||||||
# https://github.com/buildkite-plugins/docker-buildkite-plugin/releases
|
# https://github.com/buildkite-plugins/docker-buildkite-plugin/releases
|
||||||
|
# We propagate the environment to the container (sse https://github.com/buildkite-plugins/docker-buildkite-plugin#propagate-environment-optional-boolean)
|
||||||
# Build debug version of the RiotX application, from the develop branch and the features branches
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- label: "Assemble GPlay Debug version"
|
- label: "Compile and run Unit tests"
|
||||||
agents:
|
agents:
|
||||||
# We use a medium sized instance instead of the normal small ones because
|
# We use a medium sized instance instead of the normal small ones because
|
||||||
# gradle build is long
|
# gradle build can be memory hungry
|
||||||
queue: "medium"
|
queue: "medium"
|
||||||
|
commands:
|
||||||
|
- "./gradlew clean test --stacktrace"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.1.0:
|
||||||
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
|
- label: "Compile Android tests"
|
||||||
|
agents:
|
||||||
|
# We use a medium sized instance instead of the normal small ones because
|
||||||
|
# gradle build can be memory hungry
|
||||||
|
queue: "medium"
|
||||||
|
commands:
|
||||||
|
- "./gradlew clean assembleAndroidTest --stacktrace"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.1.0:
|
||||||
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
|
- label: "Assemble GPlay Debug version"
|
||||||
|
agents:
|
||||||
|
# We use a xlarge sized instance instead of the normal small ones because
|
||||||
|
# gradle build can be memory hungry
|
||||||
|
queue: "xlarge"
|
||||||
commands:
|
commands:
|
||||||
- "./gradlew clean lintGplayRelease assembleGplayDebug --stacktrace"
|
- "./gradlew clean lintGplayRelease assembleGplayDebug --stacktrace"
|
||||||
artifact_paths:
|
artifact_paths:
|
||||||
@@ -18,12 +41,13 @@ steps:
|
|||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
image: "runmymind/docker-android-sdk"
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
- label: "Assemble FDroid Debug version"
|
- label: "Assemble FDroid Debug version"
|
||||||
agents:
|
agents:
|
||||||
# We use a medium sized instance instead of the normal small ones because
|
# We use a xlarge sized instance instead of the normal small ones because
|
||||||
# gradle build is long
|
# gradle build can be memory hungry
|
||||||
queue: "medium"
|
queue: "xlarge"
|
||||||
commands:
|
commands:
|
||||||
- "./gradlew clean lintFdroidRelease assembleFdroidDebug --stacktrace"
|
- "./gradlew clean lintFdroidRelease assembleFdroidDebug --stacktrace"
|
||||||
artifact_paths:
|
artifact_paths:
|
||||||
@@ -32,12 +56,13 @@ steps:
|
|||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
image: "runmymind/docker-android-sdk"
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
- label: "Build Google Play unsigned APK"
|
- label: "Build Google Play unsigned APK"
|
||||||
agents:
|
agents:
|
||||||
# We use a medium sized instance instead of the normal small ones because
|
# We use a xlarge sized instance instead of the normal small ones because
|
||||||
# gradle build is long
|
# gradle build can be memory hungry
|
||||||
queue: "medium"
|
queue: "xlarge"
|
||||||
commands:
|
commands:
|
||||||
- "./gradlew clean assembleGplayRelease --stacktrace"
|
- "./gradlew clean assembleGplayRelease --stacktrace"
|
||||||
artifact_paths:
|
artifact_paths:
|
||||||
@@ -46,8 +71,18 @@ steps:
|
|||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
image: "runmymind/docker-android-sdk"
|
image: "runmymind/docker-android-sdk"
|
||||||
|
propagate-environment: true
|
||||||
|
|
||||||
# Code quality
|
# Code quality
|
||||||
|
|
||||||
- label: "Code quality"
|
- label: "Code quality"
|
||||||
command: "./tools/check/check_code_quality.sh"
|
command:
|
||||||
|
- "./tools/check/check_code_quality.sh"
|
||||||
|
|
||||||
|
- label: "ktlint"
|
||||||
|
command:
|
||||||
|
- "curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint"
|
||||||
|
- "./ktlint --android --experimental -v"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.1.0:
|
||||||
|
image: "openjdk"
|
||||||
|
32
.editorconfig
Normal file
32
.editorconfig
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# For ktlint configuration. Ref: https://ktlint.github.io/
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
# possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
|
||||||
|
indent_size=unset
|
||||||
|
# true (recommended) / false
|
||||||
|
insert_final_newline=true
|
||||||
|
# possible values: number (e.g. 120) (package name, imports & comments are ignored), "off"
|
||||||
|
# it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide)
|
||||||
|
max_line_length=off
|
||||||
|
|
||||||
|
# Comma-separated list of rules to disable (Since 0.34.0)
|
||||||
|
# Note that rules in any ruleset other than the standard ruleset will need to be prefixed
|
||||||
|
# by the ruleset identifier.
|
||||||
|
disabled_rules=no-wildcard-imports,no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation
|
||||||
|
|
||||||
|
# The following (so far identified) rules are kept:
|
||||||
|
# no-blank-line-before-rbrace
|
||||||
|
# final-newline
|
||||||
|
# no-consecutive-blank-lines
|
||||||
|
# comment-spacing
|
||||||
|
# filename
|
||||||
|
# comma-spacing
|
||||||
|
# paren-spacing
|
||||||
|
# op-spacing
|
||||||
|
# string-template
|
||||||
|
# no-unused-imports
|
||||||
|
# curly-spacing
|
||||||
|
# no-semi
|
||||||
|
# no-empty-class-body
|
||||||
|
# experimental:multiline-if-else
|
||||||
|
# experimental:no-empty-first-line-in-method-block
|
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,14 +1,18 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
/local.properties
|
||||||
.idea/*
|
# idea files: exclude everything except dictionnaries
|
||||||
/.idea/*
|
.idea/caches
|
||||||
/.idea/libraries
|
.idea/codeStyles
|
||||||
/.idea/modules.xml
|
.idea/libraries
|
||||||
/.idea/workspace.xml
|
.idea/*.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
|
|
||||||
/tmp
|
/tmp
|
||||||
|
|
||||||
|
ktlint
|
||||||
|
.idea/copyright/New_vector.xml
|
||||||
|
.idea/copyright/profiles_settings.xml
|
||||||
|
24
.idea/dictionaries/bmarty.xml
generated
Normal file
24
.idea/dictionaries/bmarty.xml
generated
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="bmarty">
|
||||||
|
<words>
|
||||||
|
<w>backstack</w>
|
||||||
|
<w>bytearray</w>
|
||||||
|
<w>checkables</w>
|
||||||
|
<w>ciphertext</w>
|
||||||
|
<w>coroutine</w>
|
||||||
|
<w>decryptor</w>
|
||||||
|
<w>emoji</w>
|
||||||
|
<w>emojis</w>
|
||||||
|
<w>hmac</w>
|
||||||
|
<w>ktlint</w>
|
||||||
|
<w>linkified</w>
|
||||||
|
<w>linkify</w>
|
||||||
|
<w>megolm</w>
|
||||||
|
<w>msisdn</w>
|
||||||
|
<w>pbkdf</w>
|
||||||
|
<w>pkcs</w>
|
||||||
|
<w>signin</w>
|
||||||
|
<w>signup</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
208
CHANGES.md
208
CHANGES.md
@@ -1,3 +1,199 @@
|
|||||||
|
Changes in RiotX 0.10.0 (2019-12-10)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features ✨:
|
||||||
|
- Breadcrumbs: switch from one room to another quickly (#571)
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Support entering a RiotWeb client URL instead of the homeserver URL during connection (#744)
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Add reason for all membership events (https://github.com/matrix-org/matrix-doc/pull/2367)
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- When automardown is ON, pills are sent as MD in body (#739)
|
||||||
|
- "ban" event are not rendered correctly (#716)
|
||||||
|
- Fix crash when rotating screen in Room timeline
|
||||||
|
|
||||||
|
Changes in RiotX 0.9.1 (2019-12-05)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix an issue with DB transaction (#740)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Send mention Pills from composer
|
||||||
|
- Links in message preview in the bottom sheet are now active.
|
||||||
|
- Rework the read marker to make it more usable
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Fix a small grammatical error when an empty room list is shown.
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Do not show long click help if only invitation are displayed
|
||||||
|
- Fix emoji filtering not working
|
||||||
|
- Fix issue of closing Realm in another thread (#725)
|
||||||
|
- Attempt to properly cancel the crypto module when user signs out (#724)
|
||||||
|
|
||||||
|
Changes in RiotX 0.8.0 (2019-11-19)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features ✨:
|
||||||
|
- Handle long click on room in the room list (#395)
|
||||||
|
- Ignore/UnIgnore users, and display list of ignored users (#542, #617)
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Search reaction by name or keyword in emoji picker
|
||||||
|
- Handle code tags (#567)
|
||||||
|
- Support spoiler messages
|
||||||
|
- Support m.sticker and m.room.join_rules events in timeline
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Markdown set to off by default (#412)
|
||||||
|
- Accessibility improvements to the attachment file type chooser
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix issues with some member events rendering (#498)
|
||||||
|
- Passphrase does not match (Export room keys) (#644)
|
||||||
|
- Ask for permission to write external storage when uri comes from the keyboard (#658)
|
||||||
|
- Fix issue with english US/GB translation (#671)
|
||||||
|
|
||||||
|
Changes in RiotX 0.7.0 (2019-10-24)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Share elements from other app to RiotX (#58)
|
||||||
|
- Read marker (#84)
|
||||||
|
- Add ability to report content (#515)
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- Persist active tab between sessions (#503)
|
||||||
|
- Do not upload file too big for the homeserver (#587)
|
||||||
|
- Attachments: start using system pickers (#52)
|
||||||
|
- Mark all messages as read (#396)
|
||||||
|
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Accessibility improvements to read receipts in the room timeline and reactions emoji chooser
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix issue on upload error in loop (#587)
|
||||||
|
- Fix opening a permalink: the targeted event is displayed twice (#556)
|
||||||
|
- Fix opening a permalink paginates all the history up to the last event (#282)
|
||||||
|
- after login, the icon in the top left is a green 'A' for (all communities) rather than my avatar (#267)
|
||||||
|
- Picture uploads are unreliable, pictures are shown in wrong aspect ratio on desktop client (#517)
|
||||||
|
- Invitation notifications are not dismissed automatically if room is joined from another client (#347)
|
||||||
|
- Opening links from RiotX reuses browser tab (#599)
|
||||||
|
|
||||||
|
Changes in RiotX 0.6.1 (2019-09-24)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix crash: MergedHeaderItem was missing dimensionConverter
|
||||||
|
|
||||||
|
Changes in RiotX 0.6.0 (2019-09-24)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Save draft of a message when exiting a room with non empty composer (#329)
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- Add unread indent on room list (#485)
|
||||||
|
- Message Editing: Update notifications (#128)
|
||||||
|
- Remove any notification of a redacted event (#563)
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Fix a few accessibility issues
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix characters erased from the Search field when the result are coming (#545)
|
||||||
|
- "No connection" banner was displayed by mistake
|
||||||
|
- Leaving community (from another client) has no effect on RiotX (#497)
|
||||||
|
- Push rules was not retrieved after a clear cache
|
||||||
|
- m.notice messages trigger push notifications (#238)
|
||||||
|
- Embiggen messages with multiple emojis also for edited messages (#458)
|
||||||
|
|
||||||
|
Build:
|
||||||
|
- Fix (again) issue with bad versionCode generated by Buildkite (#553)
|
||||||
|
|
||||||
|
Changes in RiotX 0.5.0 (2019-09-17)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Implementation of login to homeserver with SSO (#557)
|
||||||
|
- Handle M_CONSENT_NOT_GIVEN error (#64)
|
||||||
|
- Auto configure homeserver and identity server URLs of LoginActivity with a magic link
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- Reduce default release build log level, and lab option to enable more logs.
|
||||||
|
- Display a no network indicator when there is no network (#559)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix crash due to missing informationData (#535)
|
||||||
|
- Progress in initial sync dialog is decreasing for a step and should not (#532)
|
||||||
|
- Fix rendering issue of accepted third party invitation event
|
||||||
|
- All current notifications were dismissed by mistake when the app is launched from the launcher
|
||||||
|
|
||||||
|
Build:
|
||||||
|
- Fix issue with version name (#533)
|
||||||
|
- Fix issue with bad versionCode generated by Buildkite (#553)
|
||||||
|
|
||||||
|
Changes in RiotX 0.4.0 (2019-08-30)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Display read receipts in timeline (#81)
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- Reactions: Reinstate the ability to react with non-unicode keys (#307)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fix text diff linebreak display (#441)
|
||||||
|
- Date change message repeats for each redaction until a normal message (#358)
|
||||||
|
- Slide-in reply icon is distorted (#423)
|
||||||
|
- Regression / e2e replies not encrypted
|
||||||
|
- Some video won't play
|
||||||
|
- Privacy: remove log of notifiable event (#519)
|
||||||
|
- Fix crash with EmojiCompat (#530)
|
||||||
|
|
||||||
|
Changes in RiotX 0.3.0 (2019-08-08)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Create Direct Room flow
|
||||||
|
- Handle `/markdown` command
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
- UI for pending edits (#193)
|
||||||
|
- UX image preview screen transition (#393)
|
||||||
|
- Basic support for resending failed messages (retry/remove)
|
||||||
|
- Enable proper cancellation of suspending functions (including db transaction)
|
||||||
|
- Enhances network connectivity checks in SDK
|
||||||
|
- Add "View Edit History" item in the message bottom sheet (#401)
|
||||||
|
- Cancel sync request on pause and timeout to 0 after pause (#404)
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Show sync progress also in room detail screen (#403)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Edited message: link confusion when (edited) appears in body (#398)
|
||||||
|
- Close detail room screen when the room is left with another client (#256)
|
||||||
|
- Clear notification for a room left on another client
|
||||||
|
- Fix messages with empty `in_reply_to` not rendering (#447)
|
||||||
|
- Fix clear cache (#408) and Logout (#205)
|
||||||
|
- Fix `(edited)` link can be copied to clipboard (#402)
|
||||||
|
|
||||||
|
Build:
|
||||||
|
- Split APK: generate one APK per arch, to reduce APK size of about 30%
|
||||||
|
|
||||||
|
|
||||||
Changes in RiotX 0.2.0 (2019-07-18)
|
Changes in RiotX 0.2.0 (2019-07-18)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
@@ -36,24 +232,24 @@ Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-a
|
|||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
|
|
||||||
Changes in RiotX 0.XX (2019-XX-XX)
|
Changes in RiotX 0.0.0 (2019-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features:
|
Features ✨:
|
||||||
-
|
-
|
||||||
|
|
||||||
Improvements:
|
Improvements 🙌:
|
||||||
-
|
-
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
-
|
||||||
|
|
||||||
Bugfix:
|
Bugfix 🐛:
|
||||||
-
|
-
|
||||||
|
|
||||||
Translations:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
|
||||||
Build:
|
Build 🧱:
|
||||||
-
|
-
|
||||||
|
|
||||||
|
@@ -40,19 +40,45 @@ Please add a line to the top of the file `CHANGES.md` describing your change.
|
|||||||
|
|
||||||
Make sure the following commands execute without any error:
|
Make sure the following commands execute without any error:
|
||||||
|
|
||||||
> ./tools/check/check_code_quality.sh
|
#### Internal tool
|
||||||
|
|
||||||
> ./gradlew lintGplayRelease
|
<pre>
|
||||||
|
./tools/check/check_code_quality.sh
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
#### ktlint
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint
|
||||||
|
./ktlint --android --experimental -v
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Note that you can run
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
./ktlint --android --experimental -v -F
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
For ktlint to fix some detected errors for you (you still have to check and commit the fix of course)
|
||||||
|
|
||||||
|
#### lint
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
./gradlew lintGplayRelease
|
||||||
|
./gradlew lintFdroidRelease
|
||||||
|
</pre>
|
||||||
|
|
||||||
### Unit tests
|
### Unit tests
|
||||||
|
|
||||||
Make sure the following commands execute without any error:
|
Make sure the following commands execute without any error:
|
||||||
|
|
||||||
> ./gradlew testGplayReleaseUnitTest
|
<pre>
|
||||||
|
./gradlew testGplayReleaseUnitTest
|
||||||
|
</pre>
|
||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
|
|
||||||
RiotX is currently supported on Android Jelly Bean (API 16+): please test your change on an Android device (or Android emulator) running with API 16. Many issues can happen (including crashes) on older devices.
|
RiotX is currently supported on Android KitKat (API 19+): please test your change on an Android device (or Android emulator) running with API 19. Many issues can happen (including crashes) on older devices.
|
||||||
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
|
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
|
||||||
|
|
||||||
### Internationalisation
|
### Internationalisation
|
||||||
@@ -60,6 +86,10 @@ Also, if possible, please test your change on a real device. Testing on Android
|
|||||||
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).
|
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).
|
||||||
Do not hesitate to use plurals when appropriate.
|
Do not hesitate to use plurals when appropriate.
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
|
||||||
|
Please consider accessibility as an important point. As a minimum requirement, in layout XML files please use attributes such as `android:contentDescription` and `android:importantForAccessibility`, and test with a screen reader if it's working well. You can add new string resources, dedicated to accessibility, in this case, please prefix theirs id with `a11y_`.
|
||||||
|
|
||||||
### Layout
|
### Layout
|
||||||
|
|
||||||
When adding or editing layouts, make sure the layout will render correctly if device uses a RTL (Right To Left) language.
|
When adding or editing layouts, make sure the layout will render correctly if device uses a RTL (Right To Left) language.
|
||||||
|
17
build.gradle
17
build.gradle
@@ -1,9 +1,7 @@
|
|||||||
import javax.tools.JavaCompiler
|
|
||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.21'
|
ext.kotlin_version = '1.3.50'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
@@ -12,11 +10,11 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||||
classpath 'com.google.gms:google-services:4.2.0'
|
classpath 'com.google.gms:google-services:4.3.2'
|
||||||
classpath "com.airbnb.okreplay:gradle-plugin:1.4.0"
|
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.9.5'
|
classpath 'com.google.android.gms:oss-licenses-plugin:0.9.5'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
@@ -61,6 +59,11 @@ allprojects {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||||
|
// Warnings are potential errors, so stop ignoring them
|
||||||
|
kotlinOptions.allWarningsAsErrors = true
|
||||||
|
}
|
||||||
|
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
extensions.findByName("kapt")?.arguments {
|
extensions.findByName("kapt")?.arguments {
|
||||||
arg("dagger.gradle.incremental", "enabled")
|
arg("dagger.gradle.incremental", "enabled")
|
||||||
|
260
docs/signin.md
Normal file
260
docs/signin.md
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
# Sign in to a homeserver
|
||||||
|
|
||||||
|
This document describes the flow of signin to a homeserver, and also the flow when user want to reset his password. Examples come from the `matrix.org` homeserver.
|
||||||
|
|
||||||
|
## Sign up flows
|
||||||
|
|
||||||
|
### Get the flow
|
||||||
|
|
||||||
|
Client request the sign-in flows, once the homeserver is chosen by the user and its url is known (in the example it's `https://matrix.org`)
|
||||||
|
|
||||||
|
> curl -X GET 'https://matrix.org/_matrix/client/r0/login'
|
||||||
|
|
||||||
|
200
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"type": "m.login.password"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Login with username
|
||||||
|
|
||||||
|
The user is able to connect using `m.login.password`
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"identifier":{"type":"m.id.user","user":"alice"},"password":"weak_password","type":"m.login.password","initial_device_display_name":"Portable"}' 'https://matrix.org/_matrix/client/r0/login'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"identifier": {
|
||||||
|
"type": "m.id.user",
|
||||||
|
"user": "alice"
|
||||||
|
},
|
||||||
|
"password": "weak_password",
|
||||||
|
"type": "m.login.password",
|
||||||
|
"initial_device_display_name": "Portable"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Incorrect password
|
||||||
|
|
||||||
|
403
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_FORBIDDEN",
|
||||||
|
"error": "Invalid password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Correct password:
|
||||||
|
|
||||||
|
We get credential (200)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_id": "@benoit0816:matrix.org",
|
||||||
|
"access_token": "MDAxOGxvY2F0aW9uIG1hdHREDACTEDb2l0MDgxNjptYXRyaXgub3JnCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gfnYrSypfdTtkNXIuNWx1KgowMDJmc2lnbmF0dXJlIOsh1XqeAkXexh4qcofl_aR4kHJoSOWYGOhE7-ubX-DZCg",
|
||||||
|
"home_server": "matrix.org",
|
||||||
|
"device_id": "GTVREDALBF",
|
||||||
|
"well_known": {
|
||||||
|
"m.homeserver": {
|
||||||
|
"base_url": "https:\/\/matrix.org\/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Login with email
|
||||||
|
|
||||||
|
If the user has associated an email with its account, he can signin using the email.
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"identifier":{"type":"m.id.thirdparty","medium":"email","address":"alice@yopmail.com"},"password":"weak_password","type":"m.login.password","initial_device_display_name":"Portable"}' 'https://matrix.org/_matrix/client/r0/login'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"identifier": {
|
||||||
|
"type": "m.id.thirdparty",
|
||||||
|
"medium": "email",
|
||||||
|
"address": "alice@yopmail.com"
|
||||||
|
},
|
||||||
|
"password": "weak_password",
|
||||||
|
"type": "m.login.password",
|
||||||
|
"initial_device_display_name": "Portable"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Unknown email
|
||||||
|
|
||||||
|
403
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_FORBIDDEN",
|
||||||
|
"error": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Known email, wrong password
|
||||||
|
|
||||||
|
403
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_FORBIDDEN",
|
||||||
|
"error": "Invalid password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Known email, correct password
|
||||||
|
|
||||||
|
We get the credentials (200)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_id": "@alice:matrix.org",
|
||||||
|
"access_token": "MDAxOGxvY2F0aW9uIG1hdHJpeC5vcmREDACTEDZXJfaWQgPSBAYmVub2l0MDgxNjptYXRyaXgub3JnCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gNjtDY0MwRlNPSFFoOC5wOgowMDJmc2lnbmF0dXJlIGiTRm1mYLLxQywxOh3qzQVT8HoEorSokEP2u-bAwtnYCg",
|
||||||
|
"home_server": "matrix.org",
|
||||||
|
"device_id": "WBSREDASND",
|
||||||
|
"well_known": {
|
||||||
|
"m.homeserver": {
|
||||||
|
"base_url": "https:\/\/matrix.org\/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Login with Msisdn
|
||||||
|
|
||||||
|
Not supported yet in RiotX
|
||||||
|
|
||||||
|
### Login with SSO
|
||||||
|
|
||||||
|
> curl -X GET 'https://homeserver.with.sso/_matrix/client/r0/login'
|
||||||
|
|
||||||
|
200
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"type": "m.login.sso"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the user can click on "Sign in with SSO" and the web screen will be displayed on the page `https://homeserver.with.sso/_matrix/static/client/login/` and the credentials will be passed back to the native code through the JS bridge
|
||||||
|
|
||||||
|
## Reset password
|
||||||
|
|
||||||
|
Ref: `https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-account-password-email-requesttoken`
|
||||||
|
|
||||||
|
When the user has forgotten his password, he can reset it by providing an email and a new password.
|
||||||
|
|
||||||
|
Here is the flow:
|
||||||
|
|
||||||
|
### Send email
|
||||||
|
|
||||||
|
User is asked to enter the email linked to his account and a new password.
|
||||||
|
We display a warning regarding e2e.
|
||||||
|
|
||||||
|
At the first step, we do not send the password, only the email and a client secret, generated by the application
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"client_secret":"6c57f284-85e2-421b-8270-fb1795a120a7","send_attempt":0,"email":"user@domain.com"}' 'https://matrix.org/_matrix/client/r0/account/password/email/requestToken'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"client_secret": "6c57f284-85e2-421b-8270-fb1795a120a7",
|
||||||
|
"send_attempt": 0,
|
||||||
|
"email": "user@domain.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### When the email is not known
|
||||||
|
|
||||||
|
We get a 400
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_THREEPID_NOT_FOUND",
|
||||||
|
"error": "Email not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### When the email is known
|
||||||
|
|
||||||
|
We get a 200 with a `sid`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sid": "tQNbrREDACTEDldA"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the user is asked to click on the link in the email he just received, and to confirm when it's done.
|
||||||
|
|
||||||
|
During this step, the new password is sent to the homeserver.
|
||||||
|
|
||||||
|
If the user confirms before the link is clicked, we get an error:
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"type":"m.login.email.identity","threepid_creds":{"client_secret":"6c57f284-85e2-421b-8270-fb1795a120a7","sid":"tQNbrREDACTEDldA"}},"new_password":"weak_password"}' 'https://matrix.org/_matrix/client/r0/account/password'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"type": "m.login.email.identity",
|
||||||
|
"threepid_creds": {
|
||||||
|
"client_secret": "6c57f284-85e2-421b-8270-fb1795a120a7",
|
||||||
|
"sid": "tQNbrREDACTEDldA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_password": "weak_password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
401
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_UNAUTHORIZED",
|
||||||
|
"error": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### User clicks on the link
|
||||||
|
|
||||||
|
The link has the form:
|
||||||
|
|
||||||
|
https://matrix.org/_matrix/client/unstable/password_reset/email/submit_token?token=fzZLBlcqhTKeaFQFSRbsQnQCkzbwtGAD&client_secret=6c57f284-85e2-421b-8270-fb1795a120a7&sid=tQNbrREDACTEDldA
|
||||||
|
|
||||||
|
It contains the client secret, a token and the sid
|
||||||
|
|
||||||
|
When the user click the link, if validate his ownership and the new password can now be ent by the application (on user demand):
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"type":"m.login.email.identity","threepid_creds":{"client_secret":"6c57f284-85e2-421b-8270-fb1795a120a7","sid":"tQNbrREDACTEDldA"}},"new_password":"weak_password"}' 'https://matrix.org/_matrix/client/r0/account/password'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"type": "m.login.email.identity",
|
||||||
|
"threepid_creds": {
|
||||||
|
"client_secret": "6c57f284-85e2-421b-8270-fb1795a120a7",
|
||||||
|
"sid": "tQNbrREDACTEDldA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_password": "weak_password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
200
|
||||||
|
|
||||||
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
||||||
|
The password has been changed, and all the existing token are invalidated. User can now login with the new password.
|
579
docs/signup.md
Normal file
579
docs/signup.md
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
# Sign up to a homeserver
|
||||||
|
|
||||||
|
This document describes the flow of registration to a homeserver. Examples come from the `matrix.org` homeserver.
|
||||||
|
|
||||||
|
*Ref*: https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
|
||||||
|
|
||||||
|
## Sign up flows
|
||||||
|
|
||||||
|
### First step
|
||||||
|
|
||||||
|
Client request the sign-up flows, once the homeserver is chosen by the user and its url is known (in the example it's `https://matrix.org`)
|
||||||
|
|
||||||
|
> curl -X POST --data $'{}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We get the flows with a 401, which also means that the registration is possible on this homeserver.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "vwehdKMtkRedactedAMwgCACZ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAAoREDACTEDoDdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the registration is not possible, we get a 403
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_FORBIDDEN",
|
||||||
|
"error": "Registration is disabled"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 1: entering user name and password
|
||||||
|
|
||||||
|
The app is displaying a form to enter username and password.
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"initial_device_display_name":"Mobile device","username":"alice","password": "weak_password"}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"initial_device_display_name": "Mobile device",
|
||||||
|
"username": "alice",
|
||||||
|
"password": "weak_password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
401. Note that the `session` value has changed (because we did not provide the previous value in the request body), but it's ok, we will use the new value for the next steps.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAAoREDACTEDoDdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### If username already exists
|
||||||
|
|
||||||
|
We get a 400:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_USER_IN_USE",
|
||||||
|
"error": "User ID already taken."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: entering email
|
||||||
|
|
||||||
|
User is proposed to enter an email. We skip this step.
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"session":"xptUYoREDACTEDogOWAGVnbJQ","type":"m.login.dummy"}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"type": "m.login.dummy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
401
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAAoREDACTEDoDdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"completed": [
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2 bis: we enter an email
|
||||||
|
|
||||||
|
We request a token to the homeserver. The `client_secret` is generated by the application
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"client_secret":"53e679ea-oRED-ACTED-92b8-3012c49c6cfa","email":"alice@yopmail.com","send_attempt":0}' 'https://matrix.org/_matrix/client/r0/register/email/requestToken'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"client_secret": "53e679ea-oRED-ACTED-92b8-3012c49c6cfa",
|
||||||
|
"email": "alice@yopmail.com",
|
||||||
|
"send_attempt": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
200
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sid": "qlBCREDACTEDEtgxD"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"threepid_creds":{"client_secret":"53e679ea-oRED-ACTED-92b8-3012c49c6cfa","sid":"qlBCREDACTEDEtgxD"},"session":"xptUYoREDACTEDogOWAGVnbJQ","type":"m.login.email.identity"}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"threepid_creds": {
|
||||||
|
"client_secret": "53e679ea-oRED-ACTED-92b8-3012c49c6cfa",
|
||||||
|
"sid": "qlBCREDACTEDEtgxD"
|
||||||
|
},
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"type": "m.login.email.identity"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We get 401 since the email is not validated yet:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_UNAUTHORIZED",
|
||||||
|
"error": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The app is now polling on
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"threepid_creds":{"client_secret":"53e679ea-oRED-ACTED-92b8-3012c49c6cfa","sid":"qlBCREDACTEDEtgxD"},"session":"xptUYoREDACTEDogOWAGVnbJQ","type":"m.login.email.identity"}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"threepid_creds": {
|
||||||
|
"client_secret": "53e679ea-oRED-ACTED-92b8-3012c49c6cfa",
|
||||||
|
"sid": "qlBCREDACTEDEtgxD"
|
||||||
|
},
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"type": "m.login.email.identity"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We click on the link received by email `https://matrix.org/_matrix/client/unstable/registration/email/submit_token?token=vtQjQIZfwdoREDACTEDozrmKYSWlCXsJ&client_secret=53e679ea-oRED-ACTED-92b8-3012c49c6cfa&sid=qlBCREDACTEDEtgxD` which contains:
|
||||||
|
- A `token` vtQjQIZfwdoREDACTEDozrmKYSWlCXsJ
|
||||||
|
- The `client_secret`: 53e679ea-oRED-ACTED-92b8-3012c49c6cfa
|
||||||
|
- A `sid`: qlBCREDACTEDEtgxD
|
||||||
|
|
||||||
|
Once the link is clicked, the registration request (polling) returns a 401 with the following content:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAAoREDACTEDoDdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"completed": [
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Accepting T&C
|
||||||
|
|
||||||
|
User is proposed to accept T&C and he accepts them
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"session":"xptUYoREDACTEDogOWAGVnbJQ","type":"m.login.terms"}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"type": "m.login.terms"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
401
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAAoREDACTEDoDdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"completed": [
|
||||||
|
"m.login.dummy",
|
||||||
|
"m.login.terms"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Captcha
|
||||||
|
|
||||||
|
User is proposed to prove he is not a robot and he does it:
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"response":"03AOLTBLSiGS9GhFDpAMblJ2nlXOmHXqAYJ5OvHCPUjiVLBef3k9snOYI_BDC32-t4D2jv-tpvkaiEI_uloobFd9RUTPpJ7con2hMddbKjSCYqXqcUQFhzhbcX6kw8uBnh2sbwBe80_ihrHGXEoACXQkL0ki1Q0uEtOeW20YBRjbNABsZPpLNZhGIWC0QVXnQ4FouAtZrl3gOAiyM-oG3cgP6M9pcANIAC_7T2P2amAHbtsTlSR9CsazNyS-rtDR9b5MywdtnWN9Aw8fTJb8cXQk_j7nvugMxzofPjSOrPKcr8h5OqPlpUCyxxnFtag6cuaPSUwh43D2L0E-ZX7djzaY2Yh_U2n6HegFNPOQ22CJmfrKwDlodmAfMPvAXyq77n3HpoREDACTEDo3830RHF4BfkGXUaZjctgg-A1mvC17hmQmQpkG7IhDqyw0onU-0vF_-ehCjq_CcQEDpS_O3uiHJaG5xGf-0rhLm57v_wA3deugbsZuO4uTuxZZycN_mKxZ97jlDVBetl9hc_5REPbhcT1w3uzTCSx7Q","session":"xptUYoREDACTEDogOWAGVnbJQ","type":"m.login.recaptcha"}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"response": "03AOLTBLSiGS9GhFDpAMblJ2nlXOmHXqAYJ5OvHCPUjiVLBef3k9snOYI_BDC32-t4D2jv-tpvkaiEI_uloobFd9RUTPpJ7con2hMddbKjSCYqXqcUQFhzhbcX6kw8uBnh2sbwBe80_ihrHGXEoACXQkL0ki1Q0uEtOeW20YBRjbNABsZPpLNZhGIWC0QVXnQ4FouAtZrl3gOAiyM-oG3cgP6M9pcANIAC_7T2P2amAHbtsTlSR9CsazNyS-rtDR9b5MywdtnWN9Aw8fTJb8cXQk_j7nvugMxzofPjSOrPKcr8h5OqPlpUCyxxnFtag6cuaPSUwh43D2L0E-ZX7djzaY2Yh_U2n6HegFNPOQ22CJmfrKwDlodmAfMPvAXyq77n3HpoREDACTEDo3830RHF4BfkGXUaZjctgg-A1mvC17hmQmQpkG7IhDqyw0onU-0vF_-ehCjq_CcQEDpS_O3uiHJaG5xGf-0rhLm57v_wA3deugbsZuO4uTuxZZycN_mKxZ97jlDVBetl9hc_5REPbhcT1w3uzTCSx7Q",
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"type": "m.login.recaptcha"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
200
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_id": "@alice:matrix.org",
|
||||||
|
"home_server": "matrix.org",
|
||||||
|
"access_token": "MDAxOGxvY2F0aW9uIG1hdHJpeC5vcmcKMoREDACTEDo50aWZpZXIga2V5CjAwMTBjaWQgZ2VuID0gMQowMDI5Y2lkIHVzZXJfaWQgPSBAYmVub2l0eHh4eDptYXRoREDACTEDoCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gNHVSVm00aVFDaWlKdoREDACTEDoJmc2lnbmF0dXJlIOmHnTLRfxiPjhrWhS-dThUX-qAzZktfRThzH1YyAsxaCg",
|
||||||
|
"device_id": "FLBAREDAJZ"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The account is created!
|
||||||
|
|
||||||
|
### Step 5: MSISDN
|
||||||
|
|
||||||
|
Some homeservers may require the user to enter MSISDN.
|
||||||
|
|
||||||
|
On matrix.org, it's not required, and not even optional, but it's still possible for the app to add a MSISDN during the registration.
|
||||||
|
|
||||||
|
The user enter a phone number and select a country, the `client_secret` is generated by the application
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"client_secret":"d3e285f6-972a-496c-9a22-7915a2db57c7","send_attempt":1,"country":"FR","phone_number":"+33611223344"}' 'https://matrix.org/_matrix/client/r0/register/msisdn/requestToken'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"client_secret": "d3e285f6-972a-496c-9a22-7915a2db57c7",
|
||||||
|
"send_attempt": 1,
|
||||||
|
"country": "FR",
|
||||||
|
"phone_number": "+33611223344"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the msisdn is already associated to another account, you will received an error:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"errcode": "M_THREEPID_IN_USE",
|
||||||
|
"error": "Phone number is already in use"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If it is not the case, the homeserver send the SMS and returns some data, especially a `sid` and a `submit_url`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msisdn": "33611223344",
|
||||||
|
"intl_fmt": "+336 11 22 33 44",
|
||||||
|
"success": true,
|
||||||
|
"sid": "1678881798",
|
||||||
|
"submit_url": "https:\/\/matrix.org\/_matrix\/client\/unstable\/add_threepid\/msisdn\/submit_token"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you execute the register request, with the received `sid`, you get an error since the MSISDN is not validated yet:
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"type":"m.login.msisdn","session":"xptUYoREDACTEDogOWAGVnbJQ","threepid_creds":{"client_secret":"d3e285f6-972a-496c-9a22-7915a2db57c7","sid":"1678881798"}}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
"auth": {
|
||||||
|
"type": "m.login.msisdn",
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"threepid_creds": {
|
||||||
|
"client_secret": "d3e285f6-972a-496c-9a22-7915a2db57c7",
|
||||||
|
"sid": "1678881798"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There is an issue on Synapse, which return a 401, it sends too much data along with the classical MatrixError fields:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAABGdGmruw6DdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"completed": [],
|
||||||
|
"error": "",
|
||||||
|
"errcode": "M_UNAUTHORIZED"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The user receive the SMS, he can enter the SMS code in the app, which is sent using the "submit_url" received ie the response of the `requestToken` request:
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"client_secret":"d3e285f6-972a-496c-9a22-7915a2db57c7","sid":"1678881798","token":"123456"}' 'https://matrix.org/_matrix/client/unstable/add_threepid/msisdn/submit_token'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"client_secret": "d3e285f6-972a-496c-9a22-7915a2db57c7",
|
||||||
|
"sid": "1678881798",
|
||||||
|
"token": "123456"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the code is not correct, we get a 200 with:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And if the code is correct we get a 200 with:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We can now execute the registration request, to the homeserver
|
||||||
|
|
||||||
|
> curl -X POST --data $'{"auth":{"type":"m.login.msisdn","session":"xptUYoREDACTEDogOWAGVnbJQ","threepid_creds":{"client_secret":"d3e285f6-972a-496c-9a22-7915a2db57c7","sid":"1678881798"}}}' 'https://matrix.org/_matrix/client/r0/register'
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"type": "m.login.msisdn",
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"threepid_creds": {
|
||||||
|
"client_secret": "d3e285f6-972a-496c-9a22-7915a2db57c7",
|
||||||
|
"sid": "1678881798"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the homeserver consider that the `m.login.msisdn` step is completed (401):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"session": "xptUYoREDACTEDogOWAGVnbJQ",
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.dummy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.recaptcha",
|
||||||
|
"m.login.terms",
|
||||||
|
"m.login.email.identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": "6LcgI54UAAAAABGdGmruw6DdOocFpYVdjYBRe4zb"
|
||||||
|
},
|
||||||
|
"m.login.terms": {
|
||||||
|
"policies": {
|
||||||
|
"privacy_policy": {
|
||||||
|
"version": "1.0",
|
||||||
|
"en": {
|
||||||
|
"name": "Terms and Conditions",
|
||||||
|
"url": "https:\/\/matrix.org\/_matrix\/consent?v=1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"completed": [
|
||||||
|
"m.login.msisdn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Tue Mar 19 09:53:05 CET 2019
|
#Fri Sep 27 10:10:35 CEST 2019
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||||
|
@@ -5,16 +5,15 @@ apply plugin: 'kotlin-kapt'
|
|||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
|
||||||
|
// Multidex is useful for tests
|
||||||
|
multiDexEnabled true
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -29,15 +28,18 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
||||||
implementation project(":matrix-sdk-android")
|
implementation project(":matrix-sdk-android")
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
|
// Paging
|
||||||
|
implementation "androidx.paging:paging-runtime-ktx:2.1.0"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
|
@@ -1,42 +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 android.content.Context;
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
public void useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
|
||||||
|
|
||||||
assertEquals("im.vector.matrix.rx.test", appContext.getPackageName());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.android.MainThreadDisposable
|
import io.reactivex.android.MainThreadDisposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
|
||||||
private class LiveDataObservable<T>(
|
private class LiveDataObservable<T>(
|
||||||
private val liveData: LiveData<T>,
|
private val liveData: LiveData<T>,
|
||||||
@@ -57,5 +58,5 @@ private class LiveDataObservable<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> LiveData<T>.asObservable(): Observable<T> {
|
fun <T> LiveData<T>.asObservable(): Observable<T> {
|
||||||
return LiveDataObservable(this)
|
return LiveDataObservable(this).observeOn(Schedulers.computation())
|
||||||
}
|
}
|
||||||
|
@@ -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.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()
|
||||||
|
}
|
||||||
|
}
|
@@ -14,25 +14,25 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.riotx.core.utils
|
package im.vector.matrix.rx
|
||||||
|
|
||||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import io.reactivex.Observable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.SingleEmitter
|
||||||
|
|
||||||
open class RxStore<T>(defaultValue: T? = null) {
|
internal class MatrixCallbackSingle<T>(private val singleEmitter: SingleEmitter<T>) : MatrixCallback<T> {
|
||||||
|
|
||||||
private val storeSubject: BehaviorRelay<T> = if (defaultValue == null) {
|
override fun onSuccess(data: T) {
|
||||||
BehaviorRelay.create<T>()
|
singleEmitter.onSuccess(data)
|
||||||
} else {
|
|
||||||
BehaviorRelay.createDefault(defaultValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observe(): Observable<T> {
|
override fun onFailure(failure: Throwable) {
|
||||||
return storeSubject.hide().observeOn(Schedulers.computation())
|
singleEmitter.tryOnError(failure)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fun post(value: T) {
|
|
||||||
storeSubject.accept(value)
|
fun <T> Cancelable.toSingle(singleEmitter: SingleEmitter<T>) {
|
||||||
|
singleEmitter.setCancellable {
|
||||||
|
this.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.Optional
|
||||||
|
import io.reactivex.Observable
|
||||||
|
|
||||||
|
fun <T : Any> Observable<Optional<T>>.unwrap(): Observable<T> {
|
||||||
|
return filter { it.hasValue() }.map { it.get() }
|
||||||
|
}
|
@@ -18,31 +18,63 @@ package im.vector.matrix.rx
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.Room
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
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.RoomSummary
|
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.session.room.timeline.TimelineEvent
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.Single
|
||||||
|
|
||||||
class RxRoom(private val room: Room) {
|
class RxRoom(private val room: Room) {
|
||||||
|
|
||||||
fun liveRoomSummary(): Observable<RoomSummary> {
|
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
|
||||||
return room.liveRoomSummary().asObservable().observeOn(Schedulers.computation())
|
return room.getRoomSummaryLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveRoomMemberIds(): Observable<List<String>> {
|
fun liveRoomMemberIds(): Observable<List<String>> {
|
||||||
return room.getRoomMemberIdsLive().asObservable().observeOn(Schedulers.computation())
|
return room.getRoomMemberIdsLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
|
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
|
||||||
return room.getEventSummaryLive(eventId).asObservable().observeOn(Schedulers.computation())
|
return room.getEventSummaryLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
|
fun liveTimelineEvent(eventId: String): Observable<Optional<TimelineEvent>> {
|
||||||
return room.liveTimeLineEvent(eventId).asObservable().observeOn(Schedulers.computation())
|
return room.getTimeLineEventLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveReadMarker(): Observable<Optional<String>> {
|
||||||
|
return room.getReadMarkerLive().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveReadReceipt(): Observable<Optional<String>> {
|
||||||
|
return room.getMyReadReceiptLive().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
|
||||||
|
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun joinRoom(reason: String? = null,
|
||||||
|
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
||||||
|
room.join(reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveEventReadReceipts(eventId: String): Observable<List<ReadReceipt>> {
|
||||||
|
return room.getEventReadReceiptsLive(eventId).asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveDrafts(): Observable<List<UserDraft>> {
|
||||||
|
return room.getDraftsLive().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveNotificationState(): Observable<RoomNotificationState> {
|
||||||
|
return room.getLiveRoomNotificationState().asObservable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Room.rx(): RxRoom {
|
fun Room.rx(): RxRoom {
|
||||||
return RxRoom(this)
|
return RxRoom(this)
|
||||||
}
|
}
|
||||||
|
@@ -16,34 +16,73 @@
|
|||||||
|
|
||||||
package im.vector.matrix.rx
|
package im.vector.matrix.rx
|
||||||
|
|
||||||
|
import androidx.paging.PagedList
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.matrix.android.api.session.pushers.Pusher
|
import im.vector.matrix.android.api.session.pushers.Pusher
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
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.Optional
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.Single
|
||||||
|
|
||||||
class RxSession(private val session: Session) {
|
class RxSession(private val session: Session) {
|
||||||
|
|
||||||
fun liveRoomSummaries(): Observable<List<RoomSummary>> {
|
fun liveRoomSummaries(): Observable<List<RoomSummary>> {
|
||||||
return session.liveRoomSummaries().asObservable().observeOn(Schedulers.computation())
|
return session.liveRoomSummaries().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveGroupSummaries(): Observable<List<GroupSummary>> {
|
fun liveGroupSummaries(): Observable<List<GroupSummary>> {
|
||||||
return session.liveGroupSummaries().asObservable().observeOn(Schedulers.computation())
|
return session.liveGroupSummaries().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveBreadcrumbs(): Observable<List<RoomSummary>> {
|
||||||
|
return session.liveBreadcrumbs().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveSyncState(): Observable<SyncState> {
|
fun liveSyncState(): Observable<SyncState> {
|
||||||
return session.syncState().asObservable().observeOn(Schedulers.computation())
|
return session.syncState().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun livePushers(): Observable<List<Pusher>> {
|
fun livePushers(): Observable<List<Pusher>> {
|
||||||
return session.livePushers().asObservable().observeOn(Schedulers.computation())
|
return session.livePushers().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveUser(userId: String): Observable<Optional<User>> {
|
||||||
|
return session.liveUser(userId).asObservable().distinctUntilChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveUsers(): Observable<List<User>> {
|
||||||
|
return session.liveUsers().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveIgnoredUsers(): Observable<List<User>> {
|
||||||
|
return session.liveIgnoredUsers().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
|
||||||
|
return session.livePagedUsers(filter).asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createRoom(roomParams: CreateRoomParams): Single<String> = Single.create {
|
||||||
|
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchUsersDirectory(search: String,
|
||||||
|
limit: Int,
|
||||||
|
excludedUserIds: Set<String>): Single<List<User>> = Single.create {
|
||||||
|
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Session.rx(): RxSession {
|
fun Session.rx(): RxSession {
|
||||||
return RxSession(this)
|
return RxSession(this)
|
||||||
}
|
}
|
||||||
|
@@ -67,6 +67,10 @@ android {
|
|||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static def gitRevision() {
|
static def gitRevision() {
|
||||||
@@ -86,49 +90,47 @@ static def gitRevisionDate() {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
def arrow_version = "0.8.0"
|
def arrow_version = "0.8.2"
|
||||||
def support_version = '1.1.0-beta01'
|
|
||||||
def moshi_version = '1.8.0'
|
def moshi_version = '1.8.0'
|
||||||
def lifecycle_version = '2.0.0'
|
def lifecycle_version = '2.1.0'
|
||||||
def coroutines_version = "1.0.1"
|
def coroutines_version = "1.3.2"
|
||||||
def markwon_version = '3.0.0'
|
def markwon_version = '3.1.0'
|
||||||
def daggerVersion = '2.23.1'
|
def daggerVersion = '2.24'
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:1.1.0-rc01"
|
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.1.0-beta01"
|
implementation "androidx.recyclerview:recyclerview:1.1.0-beta05"
|
||||||
|
|
||||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||||
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
|
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
|
||||||
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
implementation 'com.squareup.retrofit2:converter-moshi:2.6.2'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
|
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
|
||||||
implementation 'com.novoda:merlin:1.1.6'
|
implementation 'com.novoda:merlin:1.2.0'
|
||||||
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||||
|
|
||||||
implementation "ru.noties.markwon:core:$markwon_version"
|
implementation "ru.noties.markwon:core:$markwon_version"
|
||||||
|
|
||||||
|
// Image
|
||||||
|
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
||||||
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
|
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
|
||||||
|
|
||||||
// Work
|
// Work
|
||||||
implementation "androidx.work:work-runtime-ktx:2.1.0-rc01"
|
implementation "androidx.work:work-runtime-ktx:2.3.0-alpha01"
|
||||||
|
|
||||||
// FP
|
// FP
|
||||||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
||||||
implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
|
implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
|
||||||
implementation "io.arrow-kt:arrow-effects:$arrow_version"
|
|
||||||
implementation "io.arrow-kt:arrow-effects-instances:$arrow_version"
|
|
||||||
implementation "io.arrow-kt:arrow-integration-retrofit-adapter:$arrow_version"
|
|
||||||
|
|
||||||
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
|
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
|
||||||
implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2'
|
implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2'
|
||||||
@@ -136,21 +138,25 @@ dependencies {
|
|||||||
// DI
|
// DI
|
||||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||||
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
|
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
|
||||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
|
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
|
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
|
||||||
|
|
||||||
debugImplementation 'com.airbnb.okreplay:okreplay:1.4.0'
|
// Bus
|
||||||
releaseImplementation 'com.airbnb.okreplay:noop:1.4.0'
|
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||||
androidTestImplementation 'com.airbnb.okreplay:espresso:1.4.0'
|
|
||||||
|
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
|
||||||
|
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
|
||||||
|
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation 'org.robolectric:robolectric:4.0.2'
|
testImplementation 'org.robolectric:robolectric:4.3'
|
||||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||||
testImplementation "io.mockk:mockk:1.8.13.kotlin13"
|
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||||
|
testImplementation 'io.mockk:mockk:1.9.2.kotlin12'
|
||||||
testImplementation 'org.amshove.kluent:kluent-android:1.44'
|
testImplementation 'org.amshove.kluent:kluent-android:1.44'
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||||
|
|
||||||
@@ -160,7 +166,8 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
|
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
|
||||||
androidTestImplementation "io.mockk:mockk-android:1.8.13.kotlin13"
|
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||||
|
androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12'
|
||||||
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"
|
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"
|
||||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||||
|
|
||||||
|
Binary file not shown.
@@ -17,15 +17,15 @@
|
|||||||
package im.vector.matrix.android
|
package im.vector.matrix.android
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.test.InstrumentationRegistry
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
interface InstrumentedTest {
|
interface InstrumentedTest {
|
||||||
fun context(): Context {
|
fun context(): Context {
|
||||||
return InstrumentationRegistry.getTargetContext()
|
return ApplicationProvider.getApplicationContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cacheDir(): File {
|
fun cacheDir(): File {
|
||||||
return context().cacheDir
|
return context().cacheDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android;
|
package im.vector.matrix.android;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@@ -29,4 +29,4 @@ class OkReplayRuleChainNoActivity(
|
|||||||
return RuleChain.outerRule(PermissionRule(configuration))
|
return RuleChain.outerRule(PermissionRule(configuration))
|
||||||
.around(RecorderRule(configuration))
|
.around(RecorderRule(configuration))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,4 +19,4 @@ package im.vector.matrix.android
|
|||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
|
|
||||||
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)
|
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)
|
||||||
|
@@ -17,22 +17,21 @@
|
|||||||
package im.vector.matrix.android.auth
|
package im.vector.matrix.android.auth
|
||||||
|
|
||||||
import androidx.test.annotation.UiThreadTest
|
import androidx.test.annotation.UiThreadTest
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
import androidx.test.runner.AndroidJUnit4
|
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.OkReplayRuleChainNoActivity
|
import im.vector.matrix.android.OkReplayRuleChainNoActivity
|
||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.AuthenticationService
|
||||||
import okreplay.*
|
import okreplay.*
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
internal class AuthenticatorTest : InstrumentedTest {
|
internal class AuthenticationServiceTest : InstrumentedTest {
|
||||||
|
|
||||||
lateinit var authenticator: Authenticator
|
lateinit var authenticationService: AuthenticationService
|
||||||
lateinit var okReplayInterceptor: OkReplayInterceptor
|
lateinit var okReplayInterceptor: OkReplayInterceptor
|
||||||
|
|
||||||
private val okReplayConfig = OkReplayConfig.Builder()
|
private val okReplayConfig = OkReplayConfig.Builder()
|
||||||
@@ -50,7 +49,6 @@ internal class AuthenticatorTest : InstrumentedTest {
|
|||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
@OkReplay(tape = "auth", mode = TapeMode.READ_WRITE)
|
@OkReplay(tape = "auth", mode = TapeMode.READ_WRITE)
|
||||||
fun auth() {
|
fun auth() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -59,6 +57,4 @@ internal class AuthenticatorTest : InstrumentedTest {
|
|||||||
val grantExternalStoragePermissionRule: GrantPermissionRule =
|
val grantExternalStoragePermissionRule: GrantPermissionRule =
|
||||||
GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
@@ -21,7 +21,7 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
|||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import java.util.*
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class CryptoStoreHelper {
|
internal class CryptoStoreHelper {
|
||||||
|
|
||||||
@@ -35,10 +35,10 @@ internal class CryptoStoreHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createCredential() = Credentials(
|
fun createCredential() = Credentials(
|
||||||
userId = "userId_" + Random().nextInt(),
|
userId = "userId_" + Random.nextInt(),
|
||||||
homeServer = "http://matrix.org",
|
homeServer = "http://matrix.org",
|
||||||
accessToken = "access_token",
|
accessToken = "access_token",
|
||||||
refreshToken = null,
|
refreshToken = null,
|
||||||
deviceId = "deviceId_sample"
|
deviceId = "deviceId_sample"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -114,4 +114,4 @@ class CryptoStoreTest {
|
|||||||
olmAccount1.releaseAccount()
|
olmAccount1.releaseAccount()
|
||||||
olmAccount2.releaseAccount()
|
olmAccount2.releaseAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -62,8 +62,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
JsonCanonicalizer.canonicalize("{\"a\":\"\\\"\"}"))
|
JsonCanonicalizer.canonicalize("{\"a\":\"\\\"\"}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* Test from https://matrix.org/docs/spec/appendices.html#examples
|
* Test from https://matrix.org/docs/spec/appendices.html#examples
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
@@ -74,7 +72,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
JsonCanonicalizer.canonicalize("""{}"""))
|
JsonCanonicalizer.canonicalize("""{}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg002Test() {
|
fun matrixOrg002Test() {
|
||||||
assertEquals("""{"one":1,"two":"Two"}""",
|
assertEquals("""{"one":1,"two":"Two"}""",
|
||||||
@@ -84,7 +81,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
}"""))
|
}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg003Test() {
|
fun matrixOrg003Test() {
|
||||||
assertEquals("""{"a":"1","b":"2"}""",
|
assertEquals("""{"a":"1","b":"2"}""",
|
||||||
@@ -94,14 +90,12 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
}"""))
|
}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg004Test() {
|
fun matrixOrg004Test() {
|
||||||
assertEquals("""{"a":"1","b":"2"}""",
|
assertEquals("""{"a":"1","b":"2"}""",
|
||||||
JsonCanonicalizer.canonicalize("""{"b":"2","a":"1"}"""))
|
JsonCanonicalizer.canonicalize("""{"b":"2","a":"1"}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg005Test() {
|
fun matrixOrg005Test() {
|
||||||
assertEquals("""{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}""",
|
assertEquals("""{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}""",
|
||||||
@@ -126,7 +120,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
}"""))
|
}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg006Test() {
|
fun matrixOrg006Test() {
|
||||||
assertEquals("""{"a":"日本語"}""",
|
assertEquals("""{"a":"日本語"}""",
|
||||||
@@ -135,7 +128,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
}"""))
|
}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg007Test() {
|
fun matrixOrg007Test() {
|
||||||
assertEquals("""{"日":1,"本":2}""",
|
assertEquals("""{"日":1,"本":2}""",
|
||||||
@@ -145,7 +137,6 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
}"""))
|
}"""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun matrixOrg008Test() {
|
fun matrixOrg008Test() {
|
||||||
assertEquals("""{"a":"日"}""",
|
assertEquals("""{"a":"日"}""",
|
||||||
@@ -159,4 +150,4 @@ internal class JsonCanonicalizerTest : InstrumentedTest {
|
|||||||
"a": null
|
"a": null
|
||||||
}"""))
|
}"""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,11 +19,7 @@ package im.vector.matrix.android.session.room.timeline
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.internal.database.helper.add
|
import im.vector.matrix.android.internal.database.helper.*
|
||||||
import im.vector.matrix.android.internal.database.helper.addAll
|
|
||||||
import im.vector.matrix.android.internal.database.helper.isUnlinked
|
|
||||||
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.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
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.createFakeListOfEvents
|
||||||
@@ -39,7 +35,6 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
internal class ChunkEntityTest : InstrumentedTest {
|
internal class ChunkEntityTest : InstrumentedTest {
|
||||||
|
|
||||||
@@ -52,7 +47,6 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
|
monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun add_shouldAdd_whenNotAlreadyIncluded() {
|
fun add_shouldAdd_whenNotAlreadyIncluded() {
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
@@ -198,5 +192,4 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.nextToken shouldEqual nextToken
|
chunk1.nextToken shouldEqual nextToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.session.room.timeline
|
package im.vector.matrix.android.session.room.timeline
|
||||||
|
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
|
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||||
@@ -24,7 +23,7 @@ import kotlin.random.Random
|
|||||||
|
|
||||||
internal class FakeGetContextOfEventTask constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask {
|
internal class FakeGetContextOfEventTask constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask {
|
||||||
|
|
||||||
override suspend fun execute(params: GetContextOfEventTask.Params): Try<TokenChunkEventPersistor.Result> {
|
override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
|
||||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(
|
val tokenChunkEvent = FakeTokenChunkEvent(
|
||||||
Random.nextLong(System.currentTimeMillis()).toString(),
|
Random.nextLong(System.currentTimeMillis()).toString(),
|
||||||
@@ -33,6 +32,4 @@ internal class FakeGetContextOfEventTask constructor(private val tokenChunkEvent
|
|||||||
)
|
)
|
||||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, PaginationDirection.BACKWARDS)
|
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, PaginationDirection.BACKWARDS)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.session.room.timeline
|
package im.vector.matrix.android.session.room.timeline
|
||||||
|
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -24,11 +23,9 @@ import kotlin.random.Random
|
|||||||
|
|
||||||
internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
|
internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
|
||||||
|
|
||||||
override suspend fun execute(params: PaginationTask.Params): Try<TokenChunkEventPersistor.Result> {
|
override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result {
|
||||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
||||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,4 +23,4 @@ internal data class FakeTokenChunkEvent(override val start: String?,
|
|||||||
override val end: String?,
|
override val end: String?,
|
||||||
override val events: List<Event> = emptyList(),
|
override val events: List<Event> = emptyList(),
|
||||||
override val stateEvents: List<Event> = emptyList()
|
override val stateEvents: List<Event> = emptyList()
|
||||||
) : TokenChunkEvent
|
) : TokenChunkEvent
|
||||||
|
@@ -88,6 +88,4 @@ object RoomDataHelper {
|
|||||||
roomEntity.addOrUpdate(chunkEntity)
|
roomEntity.addOrUpdate(chunkEntity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -81,6 +81,4 @@ internal class TimelineTest : InstrumentedTest {
|
|||||||
// timelineEvents.size shouldEqual initialLoad + paginationCount
|
// timelineEvents.size shouldEqual initialLoad + paginationCount
|
||||||
// timeline.dispose()
|
// timeline.dispose()
|
||||||
// }
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -22,6 +22,7 @@ import okhttp3.Interceptor
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -51,27 +52,33 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
|
|||||||
var compressed = false
|
var compressed = false
|
||||||
|
|
||||||
var curlCmd = "curl"
|
var curlCmd = "curl"
|
||||||
if (curlOptions != null) {
|
curlOptions?.let {
|
||||||
curlCmd += " " + curlOptions!!
|
curlCmd += " $it"
|
||||||
}
|
}
|
||||||
curlCmd += " -X " + request.method()
|
curlCmd += " -X " + request.method
|
||||||
|
|
||||||
val requestBody = request.body()
|
val requestBody = request.body
|
||||||
if (requestBody != null) {
|
if (requestBody != null) {
|
||||||
val buffer = Buffer()
|
if (requestBody.contentLength() > 100_000) {
|
||||||
requestBody.writeTo(buffer)
|
Timber.w("Unable to log curl command data, size is too big (${requestBody.contentLength()})")
|
||||||
var charset: Charset? = UTF8
|
// Ensure the curl command will failed
|
||||||
val contentType = requestBody.contentType()
|
curlCmd += "DATA IS TOO BIG"
|
||||||
if (contentType != null) {
|
} else {
|
||||||
charset = contentType.charset(UTF8)
|
val buffer = Buffer()
|
||||||
|
requestBody.writeTo(buffer)
|
||||||
|
var charset: Charset? = UTF8
|
||||||
|
val contentType = requestBody.contentType()
|
||||||
|
if (contentType != null) {
|
||||||
|
charset = contentType.charset(UTF8)
|
||||||
|
}
|
||||||
|
// try to keep to a single line and use a subshell to preserve any line breaks
|
||||||
|
curlCmd += " --data $'" + buffer.readString(charset!!).replace("\n", "\\n") + "'"
|
||||||
}
|
}
|
||||||
// try to keep to a single line and use a subshell to preserve any line breaks
|
|
||||||
curlCmd += " --data $'" + buffer.readString(charset!!).replace("\n", "\\n") + "'"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val headers = request.headers()
|
val headers = request.headers
|
||||||
var i = 0
|
var i = 0
|
||||||
val count = headers.size()
|
val count = headers.size
|
||||||
while (i < count) {
|
while (i < count) {
|
||||||
val name = headers.name(i)
|
val name = headers.name(i)
|
||||||
val value = headers.value(i)
|
val value = headers.value(i)
|
||||||
@@ -82,7 +89,7 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url().toString()
|
curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url.toString()
|
||||||
// Replace localhost for emulator by localhost for shell
|
// Replace localhost for emulator by localhost for shell
|
||||||
.replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/")
|
.replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/")
|
||||||
+ "'")
|
+ "'")
|
||||||
@@ -90,7 +97,7 @@ internal class CurlLoggingInterceptor @Inject constructor(private val logger: Ht
|
|||||||
// Add Json formatting
|
// Add Json formatting
|
||||||
curlCmd += " | python -m json.tool"
|
curlCmd += " | python -m json.tool"
|
||||||
|
|
||||||
logger.log("--- cURL (" + request.url() + ")")
|
logger.log("--- cURL (" + request.url + ")")
|
||||||
logger.log(curlCmd)
|
logger.log(curlCmd)
|
||||||
|
|
||||||
return chain.proceed(request)
|
return chain.proceed(request)
|
||||||
|
@@ -51,7 +51,6 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
|
|||||||
// Finally this is not a JSON string...
|
// Finally this is not a JSON string...
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (message.startsWith("[")) {
|
} else if (message.startsWith("[")) {
|
||||||
// JSON Array detected
|
// JSON Array detected
|
||||||
try {
|
try {
|
||||||
@@ -61,7 +60,6 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
|
|||||||
// Finally not JSON...
|
// Finally not JSON...
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// Else not a json string to log
|
// Else not a json string to log
|
||||||
}
|
}
|
||||||
@@ -73,4 +71,4 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
|
|||||||
Timber.v(s)
|
Timber.v(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ import androidx.work.Configuration
|
|||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.AuthenticationService
|
||||||
import im.vector.matrix.android.internal.SessionManager
|
import im.vector.matrix.android.internal.SessionManager
|
||||||
import im.vector.matrix.android.internal.di.DaggerMatrixComponent
|
import im.vector.matrix.android.internal.di.DaggerMatrixComponent
|
||||||
import im.vector.matrix.android.internal.network.UserAgentHolder
|
import im.vector.matrix.android.internal.network.UserAgentHolder
|
||||||
@@ -38,7 +38,6 @@ data class MatrixConfiguration(
|
|||||||
interface Provider {
|
interface Provider {
|
||||||
fun providesMatrixConfiguration(): MatrixConfiguration
|
fun providesMatrixConfiguration(): MatrixConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +46,7 @@ data class MatrixConfiguration(
|
|||||||
*/
|
*/
|
||||||
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
|
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
|
||||||
|
|
||||||
@Inject internal lateinit var authenticator: Authenticator
|
@Inject internal lateinit var authenticationService: AuthenticationService
|
||||||
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
||||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||||
@Inject internal lateinit var olmManager: OlmManager
|
@Inject internal lateinit var olmManager: OlmManager
|
||||||
@@ -65,8 +64,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
|||||||
|
|
||||||
fun getUserAgent() = userAgentHolder.userAgent
|
fun getUserAgent() = userAgentHolder.userAgent
|
||||||
|
|
||||||
fun authenticator(): Authenticator {
|
fun authenticationService(): AuthenticationService {
|
||||||
return authenticator
|
return authenticationService
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -98,5 +97,4 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
|||||||
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
|
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,7 @@ interface MatrixCallback<in T> {
|
|||||||
* @param data the data successfully returned from the async function
|
* @param data the data successfully returned from the async function
|
||||||
*/
|
*/
|
||||||
fun onSuccess(data: T) {
|
fun onSuccess(data: T) {
|
||||||
//no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,7 +35,6 @@ interface MatrixCallback<in T> {
|
|||||||
* @param failure the failure data returned from the async function
|
* @param failure the failure data returned from the async function
|
||||||
*/
|
*/
|
||||||
fun onFailure(failure: Throwable) {
|
fun onFailure(failure: Throwable) {
|
||||||
//no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.api
|
package im.vector.matrix.android.api
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains pattern to match the different Matrix ids
|
* This class contains pattern to match the different Matrix ids
|
||||||
*/
|
*/
|
||||||
@@ -28,7 +27,7 @@ object MatrixPatterns {
|
|||||||
// regex pattern to find matrix user ids in a string.
|
// regex pattern to find matrix user ids in a string.
|
||||||
// See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
|
// See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
|
||||||
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
|
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
|
||||||
private val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
// regex pattern to find room ids in a string.
|
// regex pattern to find room ids in a string.
|
||||||
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
|
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
|
||||||
@@ -123,9 +122,9 @@ object MatrixPatterns {
|
|||||||
*/
|
*/
|
||||||
fun isEventId(str: String?): Boolean {
|
fun isEventId(str: String?): Boolean {
|
||||||
return str != null
|
return str != null
|
||||||
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|
&& (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER
|
||||||
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3
|
||||||
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
|| str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,4 +136,22 @@ object MatrixPatterns {
|
|||||||
fun isGroupId(str: String?): Boolean {
|
fun isGroupId(str: String?): Boolean {
|
||||||
return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract server name from a matrix id
|
||||||
|
*
|
||||||
|
* @param matrixId
|
||||||
|
* @return null if not found or if matrixId is null
|
||||||
|
*/
|
||||||
|
fun extractServerNameFromId(matrixId: String?): String? {
|
||||||
|
if (matrixId == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val index = matrixId.indexOf(":")
|
||||||
|
|
||||||
|
return if (index == -1) {
|
||||||
|
null
|
||||||
|
} else matrixId.substring(index + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
|
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
||||||
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
|
import im.vector.matrix.android.api.auth.login.LoginWizard
|
||||||
|
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines methods to authenticate or to create an account to a matrix server.
|
||||||
|
*/
|
||||||
|
interface AuthenticationService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the supported login flows for this homeserver.
|
||||||
|
* This is the first method to call to be able to get a wizard to login or the create an account
|
||||||
|
*/
|
||||||
|
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
|
||||||
|
*/
|
||||||
|
fun getLoginWizard(): LoginWizard
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a RegistrationWizard, to create an matrix account on the homeserver. The login flow has to be retrieved first.
|
||||||
|
*/
|
||||||
|
fun getRegistrationWizard(): RegistrationWizard
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when login and password has been sent with success to the homeserver
|
||||||
|
*/
|
||||||
|
val isRegistrationStarted: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel pending login or pending registration
|
||||||
|
*/
|
||||||
|
fun cancelPendingLoginOrRegistration()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all pending settings, including current HomeServerConnectionConfig
|
||||||
|
*/
|
||||||
|
fun reset()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is an authenticated [Session].
|
||||||
|
* @return true if there is at least one active session.
|
||||||
|
*/
|
||||||
|
fun hasAuthenticatedSessions(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last authenticated [Session], if there is an active session.
|
||||||
|
* @return the last active session if any, or null
|
||||||
|
*/
|
||||||
|
fun getLastAuthenticatedSession(): Session?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an authenticated session. You should at least call authenticate one time before.
|
||||||
|
* If you logout, this session will no longer be valid.
|
||||||
|
*
|
||||||
|
* @param sessionParams the sessionParams to open with.
|
||||||
|
* @return the associated session if any, or null
|
||||||
|
*/
|
||||||
|
fun getSession(sessionParams: SessionParams): Session?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a session after a SSO successful login
|
||||||
|
*/
|
||||||
|
fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||||
|
credentials: Credentials,
|
||||||
|
callback: MatrixCallback<Session>): Cancelable
|
||||||
|
}
|
@@ -1,59 +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.auth
|
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface defines methods to authenticate to a matrix server.
|
|
||||||
*/
|
|
||||||
interface Authenticator {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param homeServerConnectionConfig this param is used to configure the Homeserver
|
|
||||||
* @param login the login field
|
|
||||||
* @param password the password field
|
|
||||||
* @param callback the matrix callback on which you'll receive the result of authentication.
|
|
||||||
* @return return a [Cancelable]
|
|
||||||
*/
|
|
||||||
fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String, callback: MatrixCallback<Session>): Cancelable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there is an authenticated [Session].
|
|
||||||
* @return true if there is at least one active session.
|
|
||||||
*/
|
|
||||||
fun hasAuthenticatedSessions(): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the last authenticated [Session], if there is an active session.
|
|
||||||
* @return the last active session if any, or null
|
|
||||||
*/
|
|
||||||
fun getLastAuthenticatedSession(): Session?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an authenticated session. You should at least call authenticate one time before.
|
|
||||||
* If you logout, this session will no longer be valid.
|
|
||||||
*
|
|
||||||
* @param sessionParams the sessionParams to open with.
|
|
||||||
* @return the associated session if any, or null
|
|
||||||
*/
|
|
||||||
fun getSession(sessionParams: SessionParams): Session?
|
|
||||||
}
|
|
@@ -30,4 +30,7 @@ data class Credentials(
|
|||||||
@Json(name = "home_server") val homeServer: String,
|
@Json(name = "home_server") val homeServer: String,
|
||||||
@Json(name = "access_token") val accessToken: String,
|
@Json(name = "access_token") val accessToken: String,
|
||||||
@Json(name = "refresh_token") val refreshToken: String?,
|
@Json(name = "refresh_token") val refreshToken: String?,
|
||||||
@Json(name = "device_id") val deviceId: String?)
|
@Json(name = "device_id") val deviceId: String?,
|
||||||
|
// Optional data that may contain info to override home server and/or identity server
|
||||||
|
@Json(name = "well_known") val wellKnown: WellKnown? = null
|
||||||
|
)
|
||||||
|
@@ -25,13 +25,13 @@ import okhttp3.TlsVersion
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This data class holds how to connect to a specific Homeserver.
|
* This data class holds how to connect to a specific Homeserver.
|
||||||
* It's used with [im.vector.matrix.android.api.auth.Authenticator] class.
|
* It's used with [im.vector.matrix.android.api.auth.AuthenticationService] class.
|
||||||
* You should use the [Builder] to create one.
|
* You should use the [Builder] to create one.
|
||||||
*/
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class HomeServerConnectionConfig(
|
data class HomeServerConnectionConfig(
|
||||||
val homeServerUri: Uri,
|
val homeServerUri: Uri,
|
||||||
val identityServerUri: Uri,
|
val identityServerUri: Uri? = null,
|
||||||
val antiVirusServerUri: Uri? = null,
|
val antiVirusServerUri: Uri? = null,
|
||||||
val allowedFingerprints: MutableList<Fingerprint> = ArrayList(),
|
val allowedFingerprints: MutableList<Fingerprint> = ArrayList(),
|
||||||
val shouldPin: Boolean = false,
|
val shouldPin: Boolean = false,
|
||||||
@@ -48,7 +48,7 @@ data class HomeServerConnectionConfig(
|
|||||||
class Builder {
|
class Builder {
|
||||||
|
|
||||||
private lateinit var homeServerUri: Uri
|
private lateinit var homeServerUri: Uri
|
||||||
private lateinit var identityServerUri: Uri
|
private var identityServerUri: Uri? = null
|
||||||
private var antiVirusServerUri: Uri? = null
|
private var antiVirusServerUri: Uri? = null
|
||||||
private val allowedFingerprints: MutableList<Fingerprint> = ArrayList()
|
private val allowedFingerprints: MutableList<Fingerprint> = ArrayList()
|
||||||
private var shouldPin: Boolean = false
|
private var shouldPin: Boolean = false
|
||||||
@@ -253,13 +253,5 @@ data class HomeServerConnectionConfig(
|
|||||||
forceUsageTlsVersions
|
forceUsageTlsVersions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.data
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||||
|
|
||||||
|
// Either a LoginFlowResponse, or an error if the homeserver is outdated
|
||||||
|
sealed class LoginFlowResult {
|
||||||
|
data class Success(
|
||||||
|
val loginFlowResponse: LoginFlowResponse,
|
||||||
|
val isLoginAndRegistrationSupported: Boolean,
|
||||||
|
val homeServerUrl: String
|
||||||
|
) : LoginFlowResult()
|
||||||
|
|
||||||
|
object OutdatedHomeserver : LoginFlowResult()
|
||||||
|
}
|
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.auth.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for https://matrix.org/docs/spec/client_server/latest#get-matrix-client-versions
|
||||||
|
*
|
||||||
|
* Ex:
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "unstable_features": {
|
||||||
|
* "m.lazy_load_members": true
|
||||||
|
* },
|
||||||
|
* "versions": [
|
||||||
|
* "r0.0.1",
|
||||||
|
* "r0.1.0",
|
||||||
|
* "r0.2.0",
|
||||||
|
* "r0.3.0"
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class Versions(
|
||||||
|
@Json(name = "versions")
|
||||||
|
val supportedVersions: List<String>? = null,
|
||||||
|
|
||||||
|
@Json(name = "unstable_features")
|
||||||
|
val unstableFeatures: Map<String, Boolean>? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
// MatrixClientServerAPIVersion
|
||||||
|
private const val r0_0_1 = "r0.0.1"
|
||||||
|
private const val r0_1_0 = "r0.1.0"
|
||||||
|
private const val r0_2_0 = "r0.2.0"
|
||||||
|
private const val r0_3_0 = "r0.3.0"
|
||||||
|
private const val r0_4_0 = "r0.4.0"
|
||||||
|
private const val r0_5_0 = "r0.5.0"
|
||||||
|
private const val r0_6_0 = "r0.6.0"
|
||||||
|
|
||||||
|
// MatrixVersionsFeature
|
||||||
|
private const val FEATURE_LAZY_LOAD_MEMBERS = "m.lazy_load_members"
|
||||||
|
private const val FEATURE_REQUIRE_IDENTITY_SERVER = "m.require_identity_server"
|
||||||
|
private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token"
|
||||||
|
private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the SDK supports this homeserver version
|
||||||
|
*/
|
||||||
|
fun Versions.isSupportedBySdk(): Boolean {
|
||||||
|
return supportLazyLoadMembers()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the SDK supports this homeserver version for login and registration
|
||||||
|
*/
|
||||||
|
fun Versions.isLoginAndRegistrationSupportedBySdk(): Boolean {
|
||||||
|
return !doesServerRequireIdentityServerParam()
|
||||||
|
&& doesServerAcceptIdentityAccessToken()
|
||||||
|
&& doesServerSeparatesAddAndBind()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the server support the lazy loading of room members
|
||||||
|
*
|
||||||
|
* @return true if the server support the lazy loading of room members
|
||||||
|
*/
|
||||||
|
private fun Versions.supportLazyLoadMembers(): Boolean {
|
||||||
|
return supportedVersions?.contains(r0_5_0) == true
|
||||||
|
|| unstableFeatures?.get(FEATURE_LAZY_LOAD_MEMBERS) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if the `id_server` parameter is required when registering with an 3pid,
|
||||||
|
* adding a 3pid or resetting password.
|
||||||
|
*/
|
||||||
|
private fun Versions.doesServerRequireIdentityServerParam(): Boolean {
|
||||||
|
if (supportedVersions?.contains(r0_6_0) == true) return false
|
||||||
|
return unstableFeatures?.get(FEATURE_REQUIRE_IDENTITY_SERVER) ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if the `id_access_token` parameter can be safely passed to the homeserver.
|
||||||
|
* Some homeservers may trigger errors if they are not prepared for the new parameter.
|
||||||
|
*/
|
||||||
|
private fun Versions.doesServerAcceptIdentityAccessToken(): Boolean {
|
||||||
|
return supportedVersions?.contains(r0_6_0) == true
|
||||||
|
|| unstableFeatures?.get(FEATURE_ID_ACCESS_TOKEN) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Versions.doesServerSeparatesAddAndBind(): Boolean {
|
||||||
|
return supportedVersions?.contains(r0_6_0) == true
|
||||||
|
|| unstableFeatures?.get(FEATURE_SEPARATE_ADD_AND_BIND) ?: false
|
||||||
|
}
|
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "m.homeserver": {
|
||||||
|
* "base_url": "https://matrix.org"
|
||||||
|
* },
|
||||||
|
* "m.identity_server": {
|
||||||
|
* "base_url": "https://vector.im"
|
||||||
|
* }
|
||||||
|
* "m.integrations": {
|
||||||
|
* "managers": [
|
||||||
|
* {
|
||||||
|
* "api_url": "https://integrations.example.org",
|
||||||
|
* "ui_url": "https://integrations.example.org/ui"
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* "api_url": "https://bots.example.org"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class WellKnown(
|
||||||
|
@Json(name = "m.homeserver")
|
||||||
|
var homeServer: WellKnownBaseConfig? = null,
|
||||||
|
|
||||||
|
@Json(name = "m.identity_server")
|
||||||
|
var identityServer: WellKnownBaseConfig? = null,
|
||||||
|
|
||||||
|
@Json(name = "m.integrations")
|
||||||
|
var integrations: Map<String, @JvmSuppressWildcards Any>? = null
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Returns the list of integration managers proposed
|
||||||
|
*/
|
||||||
|
fun getIntegrationManagers(): List<WellKnownManagerConfig> {
|
||||||
|
val managers = ArrayList<WellKnownManagerConfig>()
|
||||||
|
integrations?.get("managers")?.let {
|
||||||
|
(it as? ArrayList<*>)?.let { configs ->
|
||||||
|
configs.forEach { config ->
|
||||||
|
(config as? Map<*, *>)?.let { map ->
|
||||||
|
val apiUrl = map["api_url"] as? String
|
||||||
|
val uiUrl = map["ui_url"] as? String ?: apiUrl
|
||||||
|
if (apiUrl != null
|
||||||
|
&& apiUrl.startsWith("https://")
|
||||||
|
&& uiUrl!!.startsWith("https://")) {
|
||||||
|
managers.add(WellKnownManagerConfig(
|
||||||
|
apiUrl = apiUrl,
|
||||||
|
uiUrl = uiUrl
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return managers
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "base_url": "https://vector.im"
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class WellKnownBaseConfig(
|
||||||
|
@Json(name = "base_url")
|
||||||
|
val baseURL: String? = null
|
||||||
|
)
|
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.data
|
||||||
|
|
||||||
|
data class WellKnownManagerConfig(
|
||||||
|
val apiUrl : String,
|
||||||
|
val uiUrl: String
|
||||||
|
)
|
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.login
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
interface LoginWizard {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param login the login field
|
||||||
|
* @param password the password field
|
||||||
|
* @param deviceName the initial device name
|
||||||
|
* @param callback the matrix callback on which you'll receive the result of authentication.
|
||||||
|
* @return return a [Cancelable]
|
||||||
|
*/
|
||||||
|
fun login(login: String,
|
||||||
|
password: String,
|
||||||
|
deviceName: String,
|
||||||
|
callback: MatrixCallback<Session>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset user password
|
||||||
|
*/
|
||||||
|
fun resetPassword(email: String,
|
||||||
|
newPassword: String,
|
||||||
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm the new password, once the user has checked his email
|
||||||
|
*/
|
||||||
|
fun resetPasswordMailConfirmed(callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
}
|
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.registration
|
||||||
|
|
||||||
|
sealed class RegisterThreePid {
|
||||||
|
data class Email(val email: String) : RegisterThreePid()
|
||||||
|
data class Msisdn(val msisdn: String, val countryCode: String) : RegisterThreePid()
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.registration
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
|
||||||
|
// Either a session or an object containing data about registration stages
|
||||||
|
sealed class RegistrationResult {
|
||||||
|
data class Success(val session: Session) : RegistrationResult()
|
||||||
|
data class FlowResponse(val flowResult: FlowResult) : RegistrationResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FlowResult(
|
||||||
|
val missingStages: List<Stage>,
|
||||||
|
val completedStages: List<Stage>
|
||||||
|
)
|
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.registration
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
interface RegistrationWizard {
|
||||||
|
|
||||||
|
fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun acceptTerms(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun sendAgainThreePid(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
fun checkIfEmailHasBeenValidated(delayMillis: Long, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
|
val currentThreePid: String?
|
||||||
|
|
||||||
|
// True when login and password has been sent with success to the homeserver
|
||||||
|
val isRegistrationStarted: Boolean
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.auth.registration
|
||||||
|
|
||||||
|
sealed class Stage(open val mandatory: Boolean) {
|
||||||
|
|
||||||
|
// m.login.recaptcha
|
||||||
|
data class ReCaptcha(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
|
||||||
|
|
||||||
|
// m.login.oauth2
|
||||||
|
// m.login.email.identity
|
||||||
|
data class Email(override val mandatory: Boolean) : Stage(mandatory)
|
||||||
|
|
||||||
|
// m.login.msisdn
|
||||||
|
data class Msisdn(override val mandatory: Boolean) : Stage(mandatory)
|
||||||
|
|
||||||
|
// m.login.token
|
||||||
|
|
||||||
|
// m.login.dummy, can be mandatory if there is no other stages. In this case the account cannot be created by just sending a username
|
||||||
|
// and a password, the dummy stage has to be done
|
||||||
|
data class Dummy(override val mandatory: Boolean) : Stage(mandatory)
|
||||||
|
|
||||||
|
// Undocumented yet: m.login.terms
|
||||||
|
data class Terms(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
|
||||||
|
|
||||||
|
// For unknown stages
|
||||||
|
data class Other(override val mandatory: Boolean, val type: String, val params: Map<*, *>?) : Stage(mandatory)
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias TermPolicies = Map<*, *>
|
@@ -17,7 +17,6 @@
|
|||||||
package im.vector.matrix.android.api.comparators
|
package im.vector.matrix.android.api.comparators
|
||||||
|
|
||||||
import im.vector.matrix.android.api.interfaces.DatedObject
|
import im.vector.matrix.android.api.interfaces.DatedObject
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object DatedObjectComparators {
|
object DatedObjectComparators {
|
||||||
|
|
||||||
@@ -38,4 +37,4 @@ object DatedObjectComparators {
|
|||||||
(datedObject2.date - datedObject1.date).toInt()
|
(datedObject2.date - datedObject1.date).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.crypto
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
||||||
|
import im.vector.matrix.android.internal.crypto.verification.getEmojiForCode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide all the emojis used for SAS verification (for debug purpose)
|
||||||
|
*/
|
||||||
|
fun getAllVerificationEmojis(): List<EmojiRepresentation> {
|
||||||
|
return (0..63).map { getEmojiForCode(it) }
|
||||||
|
}
|
@@ -19,7 +19,6 @@ package im.vector.matrix.android.api.extensions
|
|||||||
import im.vector.matrix.android.api.comparators.DatedObjectComparators
|
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.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* MXDeviceInfo
|
* MXDeviceInfo
|
||||||
@@ -29,7 +28,6 @@ fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint()
|
|||||||
?.chunked(4)
|
?.chunked(4)
|
||||||
?.joinToString(separator = " ")
|
?.joinToString(separator = " ")
|
||||||
|
|
||||||
|
fun MutableList<DeviceInfo>.sortByLastSeen() {
|
||||||
fun List<DeviceInfo>.sortByLastSeen() {
|
sortWith(DatedObjectComparators.descComparator)
|
||||||
Collections.sort(this, DatedObjectComparators.descComparator)
|
}
|
||||||
}
|
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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 data class will be sent to the bus
|
||||||
|
data class ConsentNotGivenError(
|
||||||
|
val consentUri: String
|
||||||
|
)
|
@@ -31,15 +31,16 @@ import java.io.IOException
|
|||||||
*/
|
*/
|
||||||
sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
|
sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
|
||||||
data class Unknown(val throwable: Throwable? = null) : Failure(throwable)
|
data class Unknown(val throwable: Throwable? = null) : Failure(throwable)
|
||||||
|
data class Cancelled(val throwable: Throwable? = null) : Failure(throwable)
|
||||||
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
||||||
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
||||||
|
object SuccessError : Failure(RuntimeException(RuntimeException("SuccessResult is false")))
|
||||||
// When server send an error, but it cannot be interpreted as a MatrixError
|
// When server send an error, but it cannot be interpreted as a MatrixError
|
||||||
data class OtherServerError(val errorBody: String, val httpCode: Int) : Failure(RuntimeException(errorBody))
|
data class OtherServerError(val errorBody: String, val httpCode: Int) : Failure(RuntimeException("HTTP $httpCode: $errorBody"))
|
||||||
|
|
||||||
data class RegistrationFlowError(val registrationFlowResponse: RegistrationFlowResponse) : Failure(RuntimeException(registrationFlowResponse.toString()))
|
data class RegistrationFlowError(val registrationFlowResponse: RegistrationFlowResponse) : Failure(RuntimeException(registrationFlowResponse.toString()))
|
||||||
|
|
||||||
data class CryptoError(val error: MXCryptoError) : Failure(error)
|
data class CryptoError(val error: MXCryptoError) : Failure(error)
|
||||||
|
|
||||||
abstract class FeatureFailure : Failure()
|
abstract class FeatureFailure : Failure()
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -26,8 +26,14 @@ import com.squareup.moshi.JsonClass
|
|||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MatrixError(
|
data class MatrixError(
|
||||||
@Json(name = "errcode") val code: String,
|
@Json(name = "errcode") val code: String,
|
||||||
@Json(name = "error") val message: String
|
@Json(name = "error") val message: String,
|
||||||
) {
|
|
||||||
|
@Json(name = "consent_uri") val consentUri: String? = null,
|
||||||
|
// RESOURCE_LIMIT_EXCEEDED data
|
||||||
|
@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) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val FORBIDDEN = "M_FORBIDDEN"
|
const val FORBIDDEN = "M_FORBIDDEN"
|
||||||
@@ -55,5 +61,8 @@ data class MatrixError(
|
|||||||
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
||||||
const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
|
const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
|
||||||
const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
||||||
|
|
||||||
|
// Possible value for "limit_type"
|
||||||
|
const val LIMIT_TYPE_MAU = "monthly_active_user"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,4 +25,4 @@ interface ProgressListener {
|
|||||||
* @param total
|
* @param total
|
||||||
*/
|
*/
|
||||||
fun onProgress(progress: Int, total: Int)
|
fun onProgress(progress: Int, total: Int)
|
||||||
}
|
}
|
||||||
|
@@ -31,4 +31,4 @@ interface StepProgressListener {
|
|||||||
* @param step The current step, containing progress data if available. Else you should consider progress as indeterminate
|
* @param step The current step, containing progress data if available. Else you should consider progress as indeterminate
|
||||||
*/
|
*/
|
||||||
fun onStepProgress(step: Step)
|
fun onStepProgress(step: Step)
|
||||||
}
|
}
|
||||||
|
@@ -30,9 +30,9 @@ object MatrixLinkify {
|
|||||||
*
|
*
|
||||||
* @param spannable the text in which the matrix items has to be clickable.
|
* @param spannable the text in which the matrix items has to be clickable.
|
||||||
*/
|
*/
|
||||||
fun addLinks(spannable: Spannable?, callback: MatrixPermalinkSpan.Callback?): Boolean {
|
fun addLinks(spannable: Spannable, callback: MatrixPermalinkSpan.Callback?): Boolean {
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (spannable.isNullOrEmpty()) {
|
if (spannable.isEmpty()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val text = spannable.toString()
|
val text = spannable.toString()
|
||||||
@@ -51,5 +51,4 @@ object MatrixLinkify {
|
|||||||
}
|
}
|
||||||
return hasMatch
|
return hasMatch
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -18,6 +18,7 @@ package im.vector.matrix.android.api.permalinks
|
|||||||
|
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan.Callback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.
|
* This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.
|
||||||
@@ -34,6 +35,4 @@ class MatrixPermalinkSpan(private val url: String,
|
|||||||
override fun onClick(widget: View) {
|
override fun onClick(widget: View) {
|
||||||
callback?.onUrlClicked(url)
|
callback?.onUrlClicked(url)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -33,5 +33,4 @@ sealed class PermalinkData {
|
|||||||
data class GroupLink(val groupId: String) : PermalinkData()
|
data class GroupLink(val groupId: String) : PermalinkData()
|
||||||
|
|
||||||
data class FallbackLink(val uri: Uri) : PermalinkData()
|
data class FallbackLink(val uri: Uri) : PermalinkData()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.api.permalinks
|
package im.vector.matrix.android.api.permalinks
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,10 +47,9 @@ object PermalinkFactory {
|
|||||||
* @return the permalink, or null in case of error
|
* @return the permalink, or null in case of error
|
||||||
*/
|
*/
|
||||||
fun createPermalink(id: String): String? {
|
fun createPermalink(id: String): String? {
|
||||||
return if (TextUtils.isEmpty(id)) {
|
return if (id.isEmpty()) {
|
||||||
null
|
null
|
||||||
} else MATRIX_TO_URL_BASE + escape(id)
|
} else MATRIX_TO_URL_BASE + escape(id)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,16 +70,14 @@ object PermalinkFactory {
|
|||||||
* @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org"
|
* @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org"
|
||||||
* @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink
|
* @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink
|
||||||
*/
|
*/
|
||||||
fun getLinkedId(url: String?): String? {
|
fun getLinkedId(url: String): String? {
|
||||||
val isSupported = url != null && url.startsWith(MATRIX_TO_URL_BASE)
|
val isSupported = url.startsWith(MATRIX_TO_URL_BASE)
|
||||||
|
|
||||||
return if (isSupported) {
|
return if (isSupported) {
|
||||||
url!!.substring(MATRIX_TO_URL_BASE.length)
|
url.substring(MATRIX_TO_URL_BASE.length)
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escape '/' in id, because it is used as a separator
|
* Escape '/' in id, because it is used as a separator
|
||||||
*
|
*
|
||||||
@@ -89,6 +85,6 @@ object PermalinkFactory {
|
|||||||
* @return the escaped id
|
* @return the escaped id
|
||||||
*/
|
*/
|
||||||
private fun escape(id: String): String {
|
private fun escape(id: String): String {
|
||||||
return id.replace("/".toRegex(), "%2F")
|
return id.replace("/", "%2F")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,5 +72,4 @@ object PermalinkParser {
|
|||||||
else -> PermalinkData.FallbackLink(uri)
|
else -> PermalinkData.FallbackLink(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -18,78 +18,111 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
sealed class Action {
|
||||||
|
object Notify : Action()
|
||||||
|
object DoNotNotify : Action()
|
||||||
|
data class Sound(val sound: String = ACTION_OBJECT_VALUE_VALUE_DEFAULT) : Action()
|
||||||
|
data class Highlight(val highlight: Boolean) : Action()
|
||||||
|
}
|
||||||
|
|
||||||
class Action(val type: Type) {
|
private const val ACTION_NOTIFY = "notify"
|
||||||
|
private const val ACTION_DONT_NOTIFY = "dont_notify"
|
||||||
|
private const val ACTION_COALESCE = "coalesce"
|
||||||
|
|
||||||
enum class Type(val value: String) {
|
// Ref: https://matrix.org/docs/spec/client_server/latest#tweaks
|
||||||
NOTIFY("notify"),
|
private const val ACTION_OBJECT_SET_TWEAK_KEY = "set_tweak"
|
||||||
DONT_NOTIFY("dont_notify"),
|
|
||||||
COALESCE("coalesce"),
|
|
||||||
SET_TWEAK("set_tweak");
|
|
||||||
|
|
||||||
companion object {
|
private const val ACTION_OBJECT_SET_TWEAK_VALUE_SOUND = "sound"
|
||||||
|
private const val ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT = "highlight"
|
||||||
|
|
||||||
fun safeValueOf(value: String): Type? {
|
private const val ACTION_OBJECT_VALUE_KEY = "value"
|
||||||
try {
|
private const val ACTION_OBJECT_VALUE_VALUE_DEFAULT = "default"
|
||||||
return valueOf(value)
|
|
||||||
} catch (e: IllegalArgumentException) {
|
/**
|
||||||
return null
|
* Ref: https://matrix.org/docs/spec/client_server/latest#actions
|
||||||
}
|
*
|
||||||
|
* Convert
|
||||||
|
* <pre>
|
||||||
|
* "actions": [
|
||||||
|
* "notify",
|
||||||
|
* {
|
||||||
|
* "set_tweak": "sound",
|
||||||
|
* "value": "default"
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* "set_tweak": "highlight"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* To
|
||||||
|
* [
|
||||||
|
* Action.Notify,
|
||||||
|
* Action.Sound("default"),
|
||||||
|
* Action.Highlight(true)
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||||
|
fun List<Action>.toJson(): List<Any> {
|
||||||
|
return map { action ->
|
||||||
|
when (action) {
|
||||||
|
is Action.Notify -> ACTION_NOTIFY
|
||||||
|
is Action.DoNotNotify -> ACTION_DONT_NOTIFY
|
||||||
|
is Action.Sound -> {
|
||||||
|
mapOf(
|
||||||
|
ACTION_OBJECT_SET_TWEAK_KEY to ACTION_OBJECT_SET_TWEAK_VALUE_SOUND,
|
||||||
|
ACTION_OBJECT_VALUE_KEY to action.sound
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
is Action.Highlight -> {
|
||||||
}
|
mapOf(
|
||||||
|
ACTION_OBJECT_SET_TWEAK_KEY to ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT,
|
||||||
var tweak_action: String? = null
|
ACTION_OBJECT_VALUE_KEY to action.highlight
|
||||||
var stringValue: String? = null
|
)
|
||||||
var boolValue: Boolean? = null
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun mapFrom(pushRule: PushRule): List<Action>? {
|
|
||||||
val actions = ArrayList<Action>()
|
|
||||||
pushRule.actions.forEach { actionStrOrObj ->
|
|
||||||
if (actionStrOrObj is String) {
|
|
||||||
when (actionStrOrObj) {
|
|
||||||
Action.Type.NOTIFY.value -> Action(Action.Type.NOTIFY)
|
|
||||||
Action.Type.DONT_NOTIFY.value -> Action(Action.Type.DONT_NOTIFY)
|
|
||||||
else -> {
|
|
||||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}?.let {
|
|
||||||
actions.add(it)
|
|
||||||
}
|
|
||||||
} else if (actionStrOrObj is Map<*, *>) {
|
|
||||||
val tweakAction = actionStrOrObj["set_tweak"] as? String
|
|
||||||
when (tweakAction) {
|
|
||||||
"sound" -> {
|
|
||||||
(actionStrOrObj["value"] as? String)?.let { stringValue ->
|
|
||||||
Action(Action.Type.SET_TWEAK).also {
|
|
||||||
it.tweak_action = "sound"
|
|
||||||
it.stringValue = stringValue
|
|
||||||
actions.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"highlight" -> {
|
|
||||||
(actionStrOrObj["value"] as? Boolean)?.let { boolValue ->
|
|
||||||
Action(Action.Type.SET_TWEAK).also {
|
|
||||||
it.tweak_action = "highlight"
|
|
||||||
it.boolValue = boolValue
|
|
||||||
actions.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return if (actions.isEmpty()) null else actions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun PushRule.getActions(): List<Action> {
|
||||||
|
val result = ArrayList<Action>()
|
||||||
|
|
||||||
|
actions.forEach { actionStrOrObj ->
|
||||||
|
when (actionStrOrObj) {
|
||||||
|
ACTION_NOTIFY -> Action.Notify
|
||||||
|
ACTION_DONT_NOTIFY -> Action.DoNotNotify
|
||||||
|
is Map<*, *> -> {
|
||||||
|
when (actionStrOrObj[ACTION_OBJECT_SET_TWEAK_KEY]) {
|
||||||
|
ACTION_OBJECT_SET_TWEAK_VALUE_SOUND -> {
|
||||||
|
(actionStrOrObj[ACTION_OBJECT_VALUE_KEY] as? String)?.let { stringValue ->
|
||||||
|
Action.Sound(stringValue)
|
||||||
|
}
|
||||||
|
// When the value is not there, default sound (not specified by the spec)
|
||||||
|
?: Action.Sound(ACTION_OBJECT_VALUE_VALUE_DEFAULT)
|
||||||
|
}
|
||||||
|
ACTION_OBJECT_SET_TWEAK_VALUE_HIGHLIGHT -> {
|
||||||
|
(actionStrOrObj[ACTION_OBJECT_VALUE_KEY] as? Boolean)?.let { boolValue ->
|
||||||
|
Action.Highlight(boolValue)
|
||||||
|
}
|
||||||
|
// When the value is not there, default is true, says the spec
|
||||||
|
?: Action.Highlight(true)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Timber.w("Unsupported set_tweak value ${actionStrOrObj[ACTION_OBJECT_SET_TWEAK_KEY]}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Timber.w("Unsupported action type $actionStrOrObj")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}?.let {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@@ -35,9 +35,7 @@ abstract class Condition(val kind: Kind) {
|
|||||||
else -> UNRECOGNIZE
|
else -> UNRECOGNIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun isSatisfied(conditionResolver: ConditionResolver): Boolean
|
abstract fun isSatisfied(conditionResolver: ConditionResolver): Boolean
|
||||||
@@ -45,4 +43,4 @@ abstract class Condition(val kind: Kind) {
|
|||||||
open fun technicalDescription(): String {
|
open fun technicalDescription(): String {
|
||||||
return "Kind: $kind"
|
return "Kind: $kind"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,4 +25,4 @@ interface ConditionResolver {
|
|||||||
fun resolveRoomMemberCountCondition(roomMemberCountCondition: RoomMemberCountCondition): Boolean
|
fun resolveRoomMemberCountCondition(roomMemberCountCondition: RoomMemberCountCondition): Boolean
|
||||||
fun resolveSenderNotificationPermissionCondition(senderNotificationPermissionCondition: SenderNotificationPermissionCondition): Boolean
|
fun resolveSenderNotificationPermissionCondition(senderNotificationPermissionCondition: SenderNotificationPermissionCondition): Boolean
|
||||||
fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition) : Boolean
|
fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition) : Boolean
|
||||||
}
|
}
|
||||||
|
@@ -15,13 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.api.pushrules
|
package im.vector.matrix.android.api.pushrules
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
|
class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
|
||||||
|
|
||||||
@@ -34,11 +32,11 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isSatisfied(event: Event, displayName: String): Boolean {
|
fun isSatisfied(event: Event, displayName: String): Boolean {
|
||||||
var message = when (event.type) {
|
val message = when (event.type) {
|
||||||
EventType.MESSAGE -> {
|
EventType.MESSAGE -> {
|
||||||
event.content.toModel<MessageContent>()
|
event.content.toModel<MessageContent>()
|
||||||
}
|
}
|
||||||
//TODO the spec says:
|
// TODO the spec says:
|
||||||
// Matches any message whose content is unencrypted and contains the user's current display name
|
// Matches any message whose content is unencrypted and contains the user's current display name
|
||||||
// EventType.ENCRYPTED -> {
|
// EventType.ENCRYPTED -> {
|
||||||
// event.root.getClearContent()?.toModel<MessageContent>()
|
// event.root.getClearContent()?.toModel<MessageContent>()
|
||||||
@@ -49,7 +47,6 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
|
|||||||
return caseInsensitiveFind(displayName, message.body)
|
return caseInsensitiveFind(displayName, message.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Returns whether a string contains an occurrence of another, as a standalone word, regardless of case.
|
* Returns whether a string contains an occurrence of another, as a standalone word, regardless of case.
|
||||||
@@ -60,20 +57,18 @@ class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
|
|||||||
*/
|
*/
|
||||||
fun caseInsensitiveFind(subString: String, longString: String): Boolean {
|
fun caseInsensitiveFind(subString: String, longString: String): Boolean {
|
||||||
// add sanity checks
|
// add sanity checks
|
||||||
if (TextUtils.isEmpty(subString) || TextUtils.isEmpty(longString)) {
|
if (subString.isEmpty() || longString.isEmpty()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = false
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val pattern = Pattern.compile("(\\W|^)" + Pattern.quote(subString) + "(\\W|$)", Pattern.CASE_INSENSITIVE)
|
val regex = Regex("(\\W|^)" + Regex.escape(subString) + "(\\W|$)", RegexOption.IGNORE_CASE)
|
||||||
res = pattern.matcher(longString).find()
|
return regex.containsMatchIn(longString)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## caseInsensitiveFind() : failed")
|
Timber.e(e, "## caseInsensitiveFind() : failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,28 +29,25 @@ class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind
|
|||||||
return "'$key' Matches '$pattern'"
|
return "'$key' Matches '$pattern'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun isSatisfied(event: Event): Boolean {
|
fun isSatisfied(event: Event): Boolean {
|
||||||
//TODO encrypted events?
|
// TODO encrypted events?
|
||||||
val rawJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>
|
val rawJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>
|
||||||
?: return false
|
?: return false
|
||||||
val value = extractField(rawJson, key) ?: return false
|
val value = extractField(rawJson, key) ?: return false
|
||||||
|
|
||||||
//Patterns with no special glob characters should be treated as having asterisks prepended
|
// Patterns with no special glob characters should be treated as having asterisks prepended
|
||||||
// and appended when testing the condition.
|
// and appended when testing the condition.
|
||||||
try {
|
try {
|
||||||
val modPattern = if (hasSpecialGlobChar(pattern)) simpleGlobToRegExp(pattern) else simpleGlobToRegExp("*$pattern*")
|
val modPattern = if (hasSpecialGlobChar(pattern)) simpleGlobToRegExp(pattern) else simpleGlobToRegExp("*$pattern*")
|
||||||
val regex = Regex(modPattern, RegexOption.DOT_MATCHES_ALL)
|
val regex = Regex(modPattern, RegexOption.DOT_MATCHES_ALL)
|
||||||
return regex.containsMatchIn(value)
|
return regex.containsMatchIn(value)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
//e.g PatternSyntaxException
|
// e.g PatternSyntaxException
|
||||||
Timber.e(e, "Failed to evaluate push condition")
|
Timber.e(e, "Failed to evaluate push condition")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun extractField(jsonObject: Map<*, *>, fieldPath: String): String? {
|
private fun extractField(jsonObject: Map<*, *>, fieldPath: String): String? {
|
||||||
val fieldParts = fieldPath.split(".")
|
val fieldParts = fieldPath.split(".")
|
||||||
if (fieldParts.isEmpty()) return null
|
if (fieldParts.isEmpty()) return null
|
||||||
@@ -77,9 +74,9 @@ class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind
|
|||||||
return glob.contains("*") || glob.contains("?")
|
return glob.contains("*") || glob.contains("?")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Very simple glob to regexp converter
|
// Very simple glob to regexp converter
|
||||||
private fun simpleGlobToRegExp(glob: String): String {
|
private fun simpleGlobToRegExp(glob: String): String {
|
||||||
var out = ""//"^"
|
var out = "" // "^"
|
||||||
for (i in 0 until glob.length) {
|
for (i in 0 until glob.length) {
|
||||||
val c = glob[i]
|
val c = glob[i]
|
||||||
when (c) {
|
when (c) {
|
||||||
@@ -90,8 +87,8 @@ class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind
|
|||||||
else -> out += c
|
else -> out += c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out += ""//'$'.toString()
|
out += "" // '$'.toString()
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,20 +18,25 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
interface PushRuleService {
|
interface PushRuleService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the push rules from the server
|
* Fetch the push rules from the server
|
||||||
*/
|
*/
|
||||||
fun fetchPushRules(scope: String = "global")
|
fun fetchPushRules(scope: String = RuleScope.GLOBAL)
|
||||||
|
|
||||||
//TODO get push rule set
|
// TODO get push rule set
|
||||||
fun getPushRules(scope: String = "global"): List<PushRule>
|
fun getPushRules(scope: String = RuleScope.GLOBAL): List<PushRule>
|
||||||
|
|
||||||
//TODO update rule
|
// TODO update rule
|
||||||
|
|
||||||
fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>)
|
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
fun addPushRuleListener(listener: PushRuleListener)
|
fun addPushRuleListener(listener: PushRuleListener)
|
||||||
|
|
||||||
@@ -41,6 +46,9 @@ interface PushRuleService {
|
|||||||
|
|
||||||
interface PushRuleListener {
|
interface PushRuleListener {
|
||||||
fun onMatchRule(event: Event, actions: List<Action>)
|
fun onMatchRule(event: Event, actions: List<Action>)
|
||||||
|
fun onRoomJoined(roomId: String)
|
||||||
|
fun onRoomLeft(roomId: String)
|
||||||
|
fun onEventRedacted(redactedEventId: String)
|
||||||
fun batchFinish()
|
fun batchFinish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,18 +18,17 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
private val regex = Pattern.compile("^(==|<=|>=|<|>)?(\\d*)$")
|
private val regex = Regex("^(==|<=|>=|<|>)?(\\d*)$")
|
||||||
|
|
||||||
class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_count) {
|
class RoomMemberCountCondition(val iz: String) : Condition(Kind.room_member_count) {
|
||||||
|
|
||||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||||
return conditionResolver.resolveRoomMemberCountCondition(this)
|
return conditionResolver.resolveRoomMemberCountCondition(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun technicalDescription(): String {
|
override fun technicalDescription(): String {
|
||||||
return "Room member count is $`is`"
|
return "Room member count is $iz"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
||||||
@@ -56,16 +55,12 @@ class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_co
|
|||||||
*/
|
*/
|
||||||
private fun parseIsField(): Pair<String?, Int>? {
|
private fun parseIsField(): Pair<String?, Int>? {
|
||||||
try {
|
try {
|
||||||
val match = regex.matcher(`is`)
|
val match = regex.find(iz) ?: return null
|
||||||
if (match.find()) {
|
val (prefix, count) = match.destructured
|
||||||
val prefix = match.group(1)
|
return prefix to count.toInt()
|
||||||
val count = match.group(2).toInt()
|
|
||||||
return prefix to count
|
|
||||||
}
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Timber.d(t)
|
Timber.d(t)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,7 @@ object RuleIds {
|
|||||||
|
|
||||||
// Default Underride Rules
|
// Default Underride Rules
|
||||||
const val RULE_ID_CALL = ".m.rule.call"
|
const val RULE_ID_CALL = ".m.rule.call"
|
||||||
const val RULE_ID_one_to_one_encrypted_room = ".m.rule.encrypted_room_one_to_one"
|
const val RULE_ID_ONE_TO_ONE_ENCRYPTED_ROOM = ".m.rule.encrypted_room_one_to_one"
|
||||||
const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one"
|
const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one"
|
||||||
const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
|
const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
|
||||||
const val RULE_ID_ENCRYPTED = ".m.rule.encrypted"
|
const val RULE_ID_ENCRYPTED = ".m.rule.encrypted"
|
||||||
|
@@ -15,12 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.api.pushrules
|
package im.vector.matrix.android.api.pushrules
|
||||||
|
|
||||||
|
object RuleScope {
|
||||||
enum class RulesetKey(val value: String) {
|
const val GLOBAL = "global"
|
||||||
CONTENT("content"),
|
}
|
||||||
OVERRIDE("override"),
|
|
||||||
ROOM("room"),
|
|
||||||
SENDER("sender"),
|
|
||||||
UNDERRIDE("underride"),
|
|
||||||
UNKNOWN("")
|
|
||||||
}
|
|
@@ -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.pushrules
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
|
||||||
|
*/
|
||||||
|
enum class RuleSetKey(val value: String) {
|
||||||
|
CONTENT("content"),
|
||||||
|
OVERRIDE("override"),
|
||||||
|
ROOM("room"),
|
||||||
|
SENDER("sender"),
|
||||||
|
UNDERRIDE("underride")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules-scope-kind-ruleid
|
||||||
|
*/
|
||||||
|
typealias RuleKind = RuleSetKey
|
@@ -18,7 +18,6 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.PowerLevels
|
import im.vector.matrix.android.api.session.room.model.PowerLevels
|
||||||
|
|
||||||
|
|
||||||
class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
|
class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
|
||||||
|
|
||||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||||
@@ -29,8 +28,7 @@ class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.se
|
|||||||
return "User power level <$key>"
|
return "User power level <$key>"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
|
fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
|
||||||
return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key)
|
return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -71,9 +71,9 @@ data class PushCondition(
|
|||||||
this.key?.let { SenderNotificationPermissionCondition(it) }
|
this.key?.let { SenderNotificationPermissionCondition(it) }
|
||||||
}
|
}
|
||||||
Condition.Kind.UNRECOGNIZE -> {
|
Condition.Kind.UNRECOGNIZE -> {
|
||||||
Timber.e("Unknwon kind $kind")
|
Timber.e("Unknown kind $kind")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ package im.vector.matrix.android.api.pushrules.rest
|
|||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class PushRule(
|
data class PushRule(
|
||||||
/**
|
/**
|
||||||
@@ -47,4 +46,3 @@ data class PushRule(
|
|||||||
*/
|
*/
|
||||||
val pattern: String? = null
|
val pattern: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -24,4 +24,4 @@ data class Ruleset(
|
|||||||
val room: List<PushRule>? = null,
|
val room: List<PushRule>? = null,
|
||||||
val sender: List<PushRule>? = null,
|
val sender: List<PushRule>? = null,
|
||||||
val underride: List<PushRule>? = null
|
val underride: List<PushRule>? = null
|
||||||
)
|
)
|
||||||
|
@@ -20,10 +20,10 @@ import androidx.lifecycle.LiveData
|
|||||||
|
|
||||||
interface InitialSyncProgressService {
|
interface InitialSyncProgressService {
|
||||||
|
|
||||||
fun getLiveStatus() : LiveData<Status?>
|
fun getInitialSyncProgressStatus() : LiveData<Status?>
|
||||||
|
|
||||||
data class Status(
|
data class Status(
|
||||||
@StringRes val statusText: Int?,
|
@StringRes val statusText: Int,
|
||||||
val percentProgress: Int = 0
|
val percentProgress: Int = 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session
|
|||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
|
import im.vector.matrix.android.api.failure.ConsentNotGivenError
|
||||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||||
import im.vector.matrix.android.api.session.cache.CacheService
|
import im.vector.matrix.android.api.session.cache.CacheService
|
||||||
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
||||||
@@ -26,9 +27,11 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
|||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.file.FileService
|
import im.vector.matrix.android.api.session.file.FileService
|
||||||
import im.vector.matrix.android.api.session.group.GroupService
|
import im.vector.matrix.android.api.session.group.GroupService
|
||||||
|
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
|
||||||
import im.vector.matrix.android.api.session.pushers.PushersService
|
import im.vector.matrix.android.api.session.pushers.PushersService
|
||||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
@@ -50,7 +53,9 @@ interface Session :
|
|||||||
FileService,
|
FileService,
|
||||||
PushRuleService,
|
PushRuleService,
|
||||||
PushersService,
|
PushersService,
|
||||||
InitialSyncProgressService {
|
InitialSyncProgressService,
|
||||||
|
HomeServerCapabilitiesService,
|
||||||
|
SecureStorageService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The params associated to the session
|
* The params associated to the session
|
||||||
@@ -63,7 +68,6 @@ interface Session :
|
|||||||
val myUserId: String
|
val myUserId: String
|
||||||
get() = sessionParams.credentials.userId
|
get() = sessionParams.credentials.userId
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method allow to open a session. It does start some service on the background.
|
* This method allow to open a session. It does start some service on the background.
|
||||||
*/
|
*/
|
||||||
@@ -87,7 +91,7 @@ interface Session :
|
|||||||
/**
|
/**
|
||||||
* This method start the sync thread.
|
* This method start the sync thread.
|
||||||
*/
|
*/
|
||||||
fun startSync(fromForeground : Boolean)
|
fun startSync(fromForeground: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method stop the sync thread.
|
* This method stop the sync thread.
|
||||||
@@ -136,6 +140,9 @@ interface Session :
|
|||||||
*/
|
*/
|
||||||
fun onInvalidToken()
|
fun onInvalidToken()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A M_CONSENT_NOT_GIVEN error has been received from the homeserver
|
||||||
|
*/
|
||||||
|
fun onConsentNotGivenError(consentNotGivenError: ConsentNotGivenError)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.cache
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines a method to sign out. It's implemented at the session level.
|
* This interface defines a method to clear the cache. It's implemented at the session level.
|
||||||
*/
|
*/
|
||||||
interface CacheService {
|
interface CacheService {
|
||||||
|
|
||||||
@@ -27,5 +27,4 @@ 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 session is closed and has to be opened again
|
||||||
*/
|
*/
|
||||||
fun clearCache(callback: MatrixCallback<Unit>)
|
fun clearCache(callback: MatrixCallback<Unit>)
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package im.vector.matrix.android.api.session.content
|
package im.vector.matrix.android.api.session.content
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -26,6 +27,7 @@ data class ContentAttachmentData(
|
|||||||
val date: Long = 0,
|
val date: Long = 0,
|
||||||
val height: Long? = 0,
|
val height: Long? = 0,
|
||||||
val width: Long? = 0,
|
val width: Long? = 0,
|
||||||
|
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
val path: String,
|
val path: String,
|
||||||
val mimeType: String,
|
val mimeType: String,
|
||||||
@@ -38,5 +40,4 @@ data class ContentAttachmentData(
|
|||||||
AUDIO,
|
AUDIO,
|
||||||
VIDEO
|
VIDEO
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -22,6 +22,8 @@ interface ContentUploadStateTracker {
|
|||||||
|
|
||||||
fun untrack(key: String, updateListener: UpdateListener)
|
fun untrack(key: String, updateListener: UpdateListener)
|
||||||
|
|
||||||
|
fun clear()
|
||||||
|
|
||||||
interface UpdateListener {
|
interface UpdateListener {
|
||||||
fun onUpdate(state: State)
|
fun onUpdate(state: State)
|
||||||
}
|
}
|
||||||
@@ -35,4 +37,4 @@ interface ContentUploadStateTracker {
|
|||||||
object Success : State()
|
object Success : State()
|
||||||
data class Failure(val throwable: Throwable) : State()
|
data class Failure(val throwable: Throwable) : State()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,4 +44,4 @@ interface ContentUrlResolver {
|
|||||||
* @return the URL to access the described resource, or null if the url is invalid.
|
* @return the URL to access the described resource, or null if the url is invalid.
|
||||||
*/
|
*/
|
||||||
fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String?
|
fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String?
|
||||||
}
|
}
|
||||||
|
@@ -26,14 +26,12 @@ import im.vector.matrix.android.api.session.events.model.Content
|
|||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
|
|
||||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
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.MXEncryptEventContentResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
interface CryptoService {
|
interface CryptoService {
|
||||||
|
|
||||||
@@ -111,10 +109,7 @@ interface CryptoService {
|
|||||||
|
|
||||||
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>)
|
fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>)
|
||||||
|
|
||||||
fun clearCryptoCache(callback: MatrixCallback<Unit>)
|
|
||||||
|
|
||||||
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
||||||
|
|
||||||
fun removeSessionListener(listener: NewSessionListener)
|
fun removeSessionListener(listener: NewSessionListener)
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -90,4 +90,4 @@ sealed class MXCryptoError : Throwable() {
|
|||||||
const val NO_MORE_ALGORITHM_REASON = "Room was previously configured to use encryption, but is no longer." +
|
const val NO_MORE_ALGORITHM_REASON = "Room was previously configured to use encryption, but is no longer." +
|
||||||
" Perhaps the homeserver is hiding the configuration event."
|
" Perhaps the homeserver is hiding the configuration event."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -210,5 +210,4 @@ interface KeysBackupService {
|
|||||||
val isEnabled: Boolean
|
val isEnabled: Boolean
|
||||||
val isStucked: Boolean
|
val isStucked: Boolean
|
||||||
val state: KeysBackupState
|
val state: KeysBackupState
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@@ -72,4 +72,4 @@ enum class KeysBackupState {
|
|||||||
WillBackUp,
|
WillBackUp,
|
||||||
// e2e keys are being sent to the homeserver
|
// e2e keys are being sent to the homeserver
|
||||||
BackingUp
|
BackingUp
|
||||||
}
|
}
|
||||||
|
@@ -23,4 +23,4 @@ interface KeysBackupStateListener {
|
|||||||
* @param newState the new state
|
* @param newState the new state
|
||||||
*/
|
*/
|
||||||
fun onStateChange(newState: KeysBackupState)
|
fun onStateChange(newState: KeysBackupState)
|
||||||
}
|
}
|
||||||
|
@@ -30,4 +30,4 @@ enum class CancelCode(val value: String, val humanReadable: String) {
|
|||||||
|
|
||||||
fun safeValueOf(code: String?): CancelCode {
|
fun safeValueOf(code: String?): CancelCode {
|
||||||
return CancelCode.values().firstOrNull { code == it.value } ?: CancelCode.User
|
return CancelCode.values().firstOrNull { code == it.value } ?: CancelCode.User
|
||||||
}
|
}
|
||||||
|
@@ -19,4 +19,4 @@ package im.vector.matrix.android.api.session.crypto.sas
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
data class EmojiRepresentation(val emoji: String,
|
data class EmojiRepresentation(val emoji: String,
|
||||||
@StringRes val nameResId: Int)
|
@StringRes val nameResId: Int)
|
||||||
|
@@ -31,4 +31,4 @@ interface IncomingSasVerificationTransaction {
|
|||||||
CANCELLED_BY_ME,
|
CANCELLED_BY_ME,
|
||||||
CANCELLED_BY_OTHER
|
CANCELLED_BY_OTHER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,4 +29,4 @@ interface OutgoingSasVerificationRequest {
|
|||||||
CANCELLED_BY_ME,
|
CANCELLED_BY_ME,
|
||||||
CANCELLED_BY_OTHER
|
CANCELLED_BY_OTHER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,4 +19,4 @@ package im.vector.matrix.android.api.session.crypto.sas
|
|||||||
object SasMode {
|
object SasMode {
|
||||||
const val DECIMAL = "decimal"
|
const val DECIMAL = "decimal"
|
||||||
const val EMOJI = "emoji"
|
const val EMOJI = "emoji"
|
||||||
}
|
}
|
@@ -36,4 +36,4 @@ interface SasVerificationService {
|
|||||||
fun transactionUpdated(tx: SasVerificationTransaction)
|
fun transactionUpdated(tx: SasVerificationTransaction)
|
||||||
fun markedAsManuallyVerified(userId: String, deviceId: String)
|
fun markedAsManuallyVerified(userId: String, deviceId: String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -47,4 +47,4 @@ interface SasVerificationTransaction {
|
|||||||
* both short codes do match
|
* both short codes do match
|
||||||
*/
|
*/
|
||||||
fun userHasVerifiedShortCode()
|
fun userHasVerifiedShortCode()
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ enum class SasVerificationTxState {
|
|||||||
Verifying,
|
Verifying,
|
||||||
Verified,
|
Verified,
|
||||||
|
|
||||||
//Global: The verification has been cancelled (by me or other), see cancelReason for details
|
// Global: The verification has been cancelled (by me or other), see cancelReason for details
|
||||||
Cancelled,
|
Cancelled,
|
||||||
OnCancelled
|
OnCancelled
|
||||||
}
|
}
|
||||||
|
@@ -34,9 +34,9 @@ import com.squareup.moshi.JsonClass
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class AggregatedAnnotation (
|
data class AggregatedAnnotation(
|
||||||
override val limited: Boolean? = false,
|
override val limited: Boolean? = false,
|
||||||
override val count: Int? = 0,
|
override val count: Int? = 0,
|
||||||
val chunk: List<RelationChunkInfo>? = null
|
val chunk: List<RelationChunkInfo>? = null
|
||||||
|
|
||||||
) : UnsignedRelationInfo
|
) : UnsignedRelationInfo
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user