mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
633 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
391ddf1925 | ||
|
e51f2b8bc1 | ||
|
2d5f57591f | ||
|
3c06517d9e | ||
|
a65f846929 | ||
|
ce6d4c4a64 | ||
|
4ee6273f2e | ||
|
061f3c6919 | ||
|
9b13381938 | ||
|
bc22647b48 | ||
|
73fccdd6de | ||
|
ad9873c565 | ||
|
e976100f1a | ||
|
289a3ab21f | ||
|
286f00396d | ||
|
2a12cf22f5 | ||
|
97daf57b0a | ||
|
697b551b9b | ||
|
0211197c47 | ||
|
6ac2717d8b | ||
|
49aed39711 | ||
|
a820443c56 | ||
|
67aa239398 | ||
|
2a3962265b | ||
|
e6fc605b08 | ||
|
8129cd0cd3 | ||
|
0bc203e0d5 | ||
|
6c9b16088f | ||
|
76b425ee8a | ||
|
5b8215a356 | ||
|
9c7df25862 | ||
|
da16ec0af3 | ||
|
1244d00b31 | ||
|
98054815a4 | ||
|
06be3e691d | ||
|
c794843bb2 | ||
|
afa3149504 | ||
|
f2f4d325eb | ||
|
fef0404ac7 | ||
|
d6a5b9fb48 | ||
|
57dba2f29a | ||
|
f64db7f5f3 | ||
|
128d3845b9 | ||
|
602ea3327b | ||
|
e55178612c | ||
|
5a0d62db6f | ||
|
ed62e6a390 | ||
|
25a6b0ddfb | ||
|
89cf1f3237 | ||
|
b65fc4f46b | ||
|
401b5e2b7a | ||
|
a44d00a31c | ||
|
267ae457ee | ||
|
887da0a3d6 | ||
|
07ffd3ded3 | ||
|
25dbb3e9ea | ||
|
68177a02e1 | ||
|
0a96841336 | ||
|
4dcd7846b0 | ||
|
dc48cd4d16 | ||
|
883a7cecf0 | ||
|
daf019b288 | ||
|
5eeb545ae2 | ||
|
618d1f5de6 | ||
|
277fc4bf61 | ||
|
c75eb050df | ||
|
304b489470 | ||
|
4709002429 | ||
|
f36bab0a7a | ||
|
68f422e498 | ||
|
2c0b7ce0f4 | ||
|
72ee1ace48 | ||
|
7c013de7b9 | ||
|
979ffcf227 | ||
|
f0f0aafa1b | ||
|
c2d5ded335 | ||
|
1a5291537d | ||
|
d6ef3ea08e | ||
|
fac9a65f54 | ||
|
93fc64ff33 | ||
|
e097c92458 | ||
|
e91dbfbf2c | ||
|
801f2860e2 | ||
|
1ffc3a4f53 | ||
|
844fe2c4cd | ||
|
69efb45fb7 | ||
|
1103d7c112 | ||
|
c88072e55f | ||
|
e041d404f3 | ||
|
5b07af2c12 | ||
|
de73539d2b | ||
|
f7ed94c6e9 | ||
|
36b1a1471a | ||
|
1f1eeccc32 | ||
|
1d449c84fe | ||
|
0750c7763c | ||
|
dbaaa07dad | ||
|
7446b12827 | ||
|
d4c8f56c6e | ||
|
2ea45185d4 | ||
|
50ba131350 | ||
|
b2df107f17 | ||
|
2b60affd9a | ||
|
e771b21ea3 | ||
|
b20bbc1295 | ||
|
609ceb7fa4 | ||
|
aac3f379a7 | ||
|
d46ae83dc4 | ||
|
561b89830a | ||
|
b69d8ad71a | ||
|
ec0a04e893 | ||
|
8307245120 | ||
|
5431584b3c | ||
|
426782a001 | ||
|
7eb9941f8c | ||
|
f585deb1d7 | ||
|
26c4c7e467 | ||
|
b2b1bebc32 | ||
|
793c0ababb | ||
|
cf1628228a | ||
|
7f433ac4a0 | ||
|
48f03c0953 | ||
|
e75d25f00c | ||
|
e22e70c5ba | ||
|
a8e4be9146 | ||
|
a9ee94e3f0 | ||
|
cdaddb15af | ||
|
a724f844f7 | ||
|
2546061933 | ||
|
f4db593009 | ||
|
ce92533525 | ||
|
6d5e1b2285 | ||
|
6388d6b91b | ||
|
54e501127c | ||
|
6bcd96d629 | ||
|
996fe71de9 | ||
|
08b38cd6ad | ||
|
d649cb4aa5 | ||
|
61be2dd3df | ||
|
b6dda73cb1 | ||
|
0c77f49ffd | ||
|
76f4a1b4f1 | ||
|
466f9bd532 | ||
|
992edb2ee2 | ||
|
8de4b0bb20 | ||
|
e21c8792e6 | ||
|
54f6440bcf | ||
|
819f0c021e | ||
|
bc2f49fa31 | ||
|
d3a3ba8079 | ||
|
1540f13444 | ||
|
245d580825 | ||
|
2578423c91 | ||
|
1762451bc0 | ||
|
38e4984a4a | ||
|
9d7c49306a | ||
|
6b605b2e98 | ||
|
cf0242f23a | ||
|
56e8325efe | ||
|
d91ed2985d | ||
|
5f9f69b4dc | ||
|
da33cbedda | ||
|
2d91865277 | ||
|
003f5fab1f | ||
|
2a365d6776 | ||
|
55f5f90c45 | ||
|
bd3bdd6996 | ||
|
0cf485d873 | ||
|
75e06d43c5 | ||
|
afa1cf7d6c | ||
|
a6f909b942 | ||
|
ae55ee82a7 | ||
|
037e53f385 | ||
|
a28dfdc48e | ||
|
81bdf506bc | ||
|
f253aa6b37 | ||
|
0702eee179 | ||
|
c4a019f0d3 | ||
|
e948e9d85a | ||
|
22c10f5ada | ||
|
869eb262f3 | ||
|
7249c7d25a | ||
|
6e8d93bc6f | ||
|
963c30a275 | ||
|
474ade01cf | ||
|
18d4b66c97 | ||
|
fa311f4ce2 | ||
|
3a9b80127f | ||
|
78c1a0acf4 | ||
|
4ee34126bd | ||
|
4f59ec37ca | ||
|
c34fea2932 | ||
|
c11a50f7ff | ||
|
23623b8895 | ||
|
b475f36b5a | ||
|
11367488e6 | ||
|
a7d8e74468 | ||
|
a25b93197d | ||
|
83f2ff5d82 | ||
|
868c056ddd | ||
|
0106adf36c | ||
|
b6a93be611 | ||
|
61291f7337 | ||
|
05013d2559 | ||
|
3240cadb94 | ||
|
594ee61a99 | ||
|
c85b0124e3 | ||
|
61bc19fc96 | ||
|
3ea3d0fc91 | ||
|
f1f1613f00 | ||
|
68dd206140 | ||
|
17e8581ef0 | ||
|
7ff45738e0 | ||
|
c9eacec449 | ||
|
3e78098c43 | ||
|
cbdacc199a | ||
|
cc01f25d8f | ||
|
9f3176c49c | ||
|
7ae2b34a9e | ||
|
d1bec21759 | ||
|
073e6227d6 | ||
|
761d8d89b0 | ||
|
f203fa5c58 | ||
|
9ccb22015a | ||
|
6d1c53c7ed | ||
|
855e7e02ce | ||
|
df3bb5b212 | ||
|
5768e45809 | ||
|
83584fab44 | ||
|
c234e12302 | ||
|
4645a8c633 | ||
|
cfb54f7608 | ||
|
7195ee9a69 | ||
|
ba6dffc0bf | ||
|
d3acf7e862 | ||
|
5c6d710f2d | ||
|
fa22a4a747 | ||
|
94aa0a02fe | ||
|
ab23d9b715 | ||
|
c5ce208d07 | ||
|
e7f7c0ba78 | ||
|
b082a009a7 | ||
|
cdeb2663fe | ||
|
a5e979b763 | ||
|
be0cadb3c7 | ||
|
6c4836e27e | ||
|
36a553a886 | ||
|
13938f2ab3 | ||
|
a755536a2f | ||
|
4a5dbde8d3 | ||
|
5744939c05 | ||
|
8c68126ed9 | ||
|
3c37d4c1b0 | ||
|
a9316b2f7e | ||
|
3e420033c5 | ||
|
571f05bcfb | ||
|
4e73705378 | ||
|
611e1185c8 | ||
|
b90d8e6238 | ||
|
ad3573226c | ||
|
1cc0825b54 | ||
|
87ac4ec5d4 | ||
|
8344506b05 | ||
|
9da3eec64f | ||
|
621c6c8773 | ||
|
d5e76c515e | ||
|
9fcf7263b5 | ||
|
0059fdf174 | ||
|
3d291c04c9 | ||
|
ca4b91a98f | ||
|
b0ba62aa31 | ||
|
abf763f454 | ||
|
15597eb041 | ||
|
00b16db7cc | ||
|
ff8a208012 | ||
|
7732bd47ce | ||
|
42a5680374 | ||
|
5d8f365520 | ||
|
938cd32ddd | ||
|
80396fcd39 | ||
|
7b97981bb5 | ||
|
b263273c87 | ||
|
427dc784fe | ||
|
7e4725c091 | ||
|
9b332f7a32 | ||
|
2b780a8b76 | ||
|
0518e5f18d | ||
|
0412b87ad1 | ||
|
2fe7caa580 | ||
|
d53650c8ae | ||
|
930b8da3b3 | ||
|
c48f153b0d | ||
|
68cd06f1fb | ||
|
c7afcf4ff2 | ||
|
3491774e7b | ||
|
ea6fde3ed0 | ||
|
267a7a36ff | ||
|
9a69bb9656 | ||
|
28aef1067b | ||
|
690a9eaa21 | ||
|
24d76b1d32 | ||
|
173458ed6c | ||
|
cebef970d3 | ||
|
4aa349ddd6 | ||
|
a918d28ded | ||
|
b1660c077e | ||
|
38075b4e8d | ||
|
174ecb10d9 | ||
|
5ef1fd62a7 | ||
|
d095784bfc | ||
|
1ac1d32b3a | ||
|
518b207f63 | ||
|
16ba5d2416 | ||
|
ac6e7652bd | ||
|
cc3e090855 | ||
|
30e4fdebd7 | ||
|
27e5626fcf | ||
|
8878cb41ee | ||
|
10575698de | ||
|
451750f08a | ||
|
6f04f4109d | ||
|
929d711149 | ||
|
b52f8b1dbf | ||
|
aa0a851b35 | ||
|
4ddc8e706d | ||
|
da2a0abf45 | ||
|
53a0e0ce10 | ||
|
7f02c0596e | ||
|
e081c3b249 | ||
|
8db4da5473 | ||
|
d389581d96 | ||
|
018574a21e | ||
|
9e3eb993ee | ||
|
6c64fb2169 | ||
|
2e70808bbd | ||
|
18bf9856fe | ||
|
f09ee5016a | ||
|
4d3c4b5afc | ||
|
7de2494af2 | ||
|
409d7e50bb | ||
|
c1222737d6 | ||
|
d02da1b97e | ||
|
a8c6b1cdf7 | ||
|
dba65dcd22 | ||
|
706736273c | ||
|
838340bbc8 | ||
|
75ec9ba3d9 | ||
|
876359539f | ||
|
6cdb192955 | ||
|
1946058c8e | ||
|
2927f1ff1c | ||
|
e230cd8ee3 | ||
|
45225e883e | ||
|
bb9a08d429 | ||
|
6ddcd046d4 | ||
|
88a4dfd094 | ||
|
7828e3f501 | ||
|
b4b302c1f2 | ||
|
487ef870f9 | ||
|
6957768567 | ||
|
7da8b13cde | ||
|
a027ef29e5 | ||
|
211c158e23 | ||
|
338650cea7 | ||
|
2d4eeb64c5 | ||
|
4f98031c6d | ||
|
3813f6d659 | ||
|
6687a74f5c | ||
|
545e13c843 | ||
|
995ec25990 | ||
|
09040b7095 | ||
|
5a69b33600 | ||
|
c8c4e10822 | ||
|
2f616cb6c5 | ||
|
776d7699bc | ||
|
f3578e2538 | ||
|
8022430f0d | ||
|
eb72d0c6d3 | ||
|
ae29cbdc34 | ||
|
cf59c7db95 | ||
|
924fac84b2 | ||
|
db0a958708 | ||
|
b31dfcfe4f | ||
|
bba2daf0fc | ||
|
23e05200b5 | ||
|
0fd8641cf6 | ||
|
26c01d46a7 | ||
|
42d1bf57f6 | ||
|
351793d456 | ||
|
03428ea9f5 | ||
|
b321838502 | ||
|
b915c91c86 | ||
|
f83b478008 | ||
|
2bdb425085 | ||
|
45a0586329 | ||
|
74283e7d50 | ||
|
6f4c307854 | ||
|
69cd0e3ea2 | ||
|
a5094f97d5 | ||
|
163c05d5cf | ||
|
903936368d | ||
|
3eba43f3fa | ||
|
d8a1939c69 | ||
|
5b74eb3bca | ||
|
071611b81c | ||
|
5461fd4060 | ||
|
5203d15409 | ||
|
989f1c6268 | ||
|
544345bbf3 | ||
|
bd9da8eaa6 | ||
|
68a5ba9a01 | ||
|
ed9ae07e52 | ||
|
9b0c2e420d | ||
|
60aaa2a39c | ||
|
8d30658fa5 | ||
|
32fd3be732 | ||
|
705b6176f1 | ||
|
d996c77c03 | ||
|
49cad8feec | ||
|
ca75eae0aa | ||
|
21271b6510 | ||
|
439029467a | ||
|
eb30b9fae9 | ||
|
38843f74ab | ||
|
91c86c1a45 | ||
|
5541c2e190 | ||
|
f2ba236130 | ||
|
4cb7754b77 | ||
|
3473a7ef5e | ||
|
c9535509e8 | ||
|
3d578c147c | ||
|
43ac66feb3 | ||
|
5e2f091ec1 | ||
|
752bde413d | ||
|
e6949c85fb | ||
|
c1cb23d728 | ||
|
4007982db6 | ||
|
a9f5ed3869 | ||
|
08964d8548 | ||
|
5186ee6e43 | ||
|
c2fc9fe0ee | ||
|
280d3f22a7 | ||
|
a19ca8a820 | ||
|
7ba1116349 | ||
|
e8862b3aaa | ||
|
a96cc19eb6 | ||
|
3d975b7fba | ||
|
d54571d0a6 | ||
|
75071cf1d9 | ||
|
4bd538e448 | ||
|
0956baecf9 | ||
|
e4968c4119 | ||
|
283e10dfef | ||
|
1c43f92e49 | ||
|
ca7796114c | ||
|
ed822becc6 | ||
|
7057b2970b | ||
|
62791e4b36 | ||
|
237cb63fc2 | ||
|
42ab7f1b4f | ||
|
8e11ba21ed | ||
|
24a9ddaa5e | ||
|
6190fb3511 | ||
|
2d9043fbed | ||
|
682dff9c1c | ||
|
9339b7e31d | ||
|
7667ef686d | ||
|
4d4fe687ac | ||
|
7152dead1d | ||
|
40b9f03132 | ||
|
19d421df84 | ||
|
c889deaab1 | ||
|
416f57b1d7 | ||
|
dda2685bd8 | ||
|
b43f3b3b6a | ||
|
a0c8a8e97c | ||
|
28bfea6af0 | ||
|
f3bc39a0c5 | ||
|
c7efd1feb9 | ||
|
c31d368d0d | ||
|
5237eb0638 | ||
|
7869d731d4 | ||
|
c603ec0b38 | ||
|
555bf1f0ae | ||
|
8bdf384563 | ||
|
19524eaa82 | ||
|
9bbae825e2 | ||
|
3e9cb987a1 | ||
|
dacb9cd86c | ||
|
b9d7333998 | ||
|
e14b507b27 | ||
|
431ac5aa2d | ||
|
78fe7e5c16 | ||
|
c2c9e37a36 | ||
|
5d3682cd44 | ||
|
2a19726e49 | ||
|
c08c652080 | ||
|
9089c54990 | ||
|
679d9bae1c | ||
|
19315fc65e | ||
|
770041eceb | ||
|
1161dcb299 | ||
|
fa7b0a24a7 | ||
|
48354c7793 | ||
|
fcd9fe7d5a | ||
|
a36d5684b8 | ||
|
be20f9b455 | ||
|
dd150c6d7e | ||
|
bd5ac514ef | ||
|
bd926fa74b | ||
|
167144b504 | ||
|
5faaabf2f4 | ||
|
1933fc948c | ||
|
83df430d17 | ||
|
ec60d7d0b6 | ||
|
f9ccb0e8de | ||
|
1109d9f88a | ||
|
32b7cc64fb | ||
|
a6724b5f75 | ||
|
8a35bfcc31 | ||
|
0c037184f8 | ||
|
3e563a37a2 | ||
|
e5cbf9e3a3 | ||
|
cafe86e675 | ||
|
a911492a9e | ||
|
d889598b20 | ||
|
c4577f28b2 | ||
|
f5af15454e | ||
|
ee96d5c68f | ||
|
6c56b5f45b | ||
|
1058bfecf4 | ||
|
f0afd5ceea | ||
|
9881c9f61c | ||
|
89a7ec6d4b | ||
|
c426364618 | ||
|
cc5264a587 | ||
|
42bc4d2445 | ||
|
2cc5d46cd3 | ||
|
3feb67ad32 | ||
|
aa6c7afbbd | ||
|
c6ba296028 | ||
|
51b86e21f6 | ||
|
04914be442 | ||
|
3133935063 | ||
|
088608011b | ||
|
6d0f9baba4 | ||
|
3b21400bb8 | ||
|
1c3a279b8a | ||
|
6236b01189 | ||
|
e42906f08a | ||
|
b53ba2b98e | ||
|
7bd8d54d5c | ||
|
c785ea63e7 | ||
|
b78f1dbb93 | ||
|
056b9df65e | ||
|
589c301606 | ||
|
476f721f5e | ||
|
a813610c04 | ||
|
41dd67f1c1 | ||
|
63b068f426 | ||
|
637c54073a | ||
|
4171311095 | ||
|
bb5d5ffc92 | ||
|
d07a95204b | ||
|
2736247d09 | ||
|
93ffb116b7 | ||
|
396dd5e36e | ||
|
096abd7ebf | ||
|
694397efc1 | ||
|
ffe9a03d3e | ||
|
b2556cb293 | ||
|
c029564590 | ||
|
0cfea40b24 | ||
|
9c53f0f881 | ||
|
50ddd3cf31 | ||
|
6c7eb6ea8c | ||
|
412fc78c9a | ||
|
0da0857970 | ||
|
90e0006cae | ||
|
a570528f6c | ||
|
2d4cbde72c | ||
|
f5ae95d7f1 | ||
|
c5e6e004dd | ||
|
d9c209aa87 | ||
|
8f80f375f0 | ||
|
2cf2233643 | ||
|
36564f0c75 | ||
|
8dbb984ead | ||
|
74ffbd4679 | ||
|
82b23d9a13 | ||
|
0a9b234272 | ||
|
3a06ef3959 | ||
|
ed4676bb6c | ||
|
893ebd9690 | ||
|
3c069f8b79 | ||
|
93580c902f | ||
|
e1abd5a051 | ||
|
27fc5f265f | ||
|
5b618ba1f3 | ||
|
a6f56ace24 | ||
|
0d93105bcd | ||
|
7c2fea8623 | ||
|
e2a89c22da | ||
|
03715e0939 | ||
|
4d9b9cb959 | ||
|
d759636d95 | ||
|
c8a8e0f2da | ||
|
b004dfbdf2 | ||
|
40ea91cce4 | ||
|
abcb02d4aa | ||
|
cd983de058 | ||
|
f6cc05634f | ||
|
f14b390849 | ||
|
431f5d76ce | ||
|
dd50399a21 | ||
|
eb17463b68 | ||
|
88e05ffd05 | ||
|
a5079f5243 | ||
|
422c681e55 | ||
|
9531a38486 | ||
|
493cd2a0e3 | ||
|
f7f7e808f2 | ||
|
217c88f342 | ||
|
cf70916764 | ||
|
b84d7f0834 | ||
|
11deaa049e | ||
|
245aa6e9e7 | ||
|
33a5cc1488 | ||
|
89e7e28bfa | ||
|
221eddd995 | ||
|
27050b911b | ||
|
a3a2c0a9a8 | ||
|
92ceb0e8fb |
3
.idea/dictionaries/bmarty.xml
generated
3
.idea/dictionaries/bmarty.xml
generated
@@ -24,6 +24,8 @@
|
||||
<w>pbkdf</w>
|
||||
<w>pids</w>
|
||||
<w>pkcs</w>
|
||||
<w>previewable</w>
|
||||
<w>previewables</w>
|
||||
<w>riotx</w>
|
||||
<w>signin</w>
|
||||
<w>signout</w>
|
||||
@@ -31,6 +33,7 @@
|
||||
<w>ssss</w>
|
||||
<w>sygnal</w>
|
||||
<w>threepid</w>
|
||||
<w>unpublish</w>
|
||||
<w>unwedging</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
|
117
CHANGES.md
117
CHANGES.md
@@ -1,3 +1,120 @@
|
||||
Changes in Element 1.0.16 (2020-02-04)
|
||||
===================================================
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix crash on API < 30 and light theme (#2774)
|
||||
|
||||
Changes in Element 1.0.15 (2020-02-03)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
- Social Login support
|
||||
|
||||
Improvements 🙌:
|
||||
- SSO support for cross signing (#1062)
|
||||
- Deactivate account when logged in with SSO (#1264)
|
||||
- SSO UIA doesn't work (#2754)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started.
|
||||
- Sidebar too large in horizontal orientation or tablets (#475)
|
||||
- UrlPreview should be updated when the url is edited and changed (#2678)
|
||||
- When receiving a new pepper from identity server, use it on the next hash lookup (#2708)
|
||||
- Crashes reported by PlayStore (new in 1.0.14) (#2707)
|
||||
- Widgets: Support $matrix_widget_id parameter (#2748)
|
||||
- Data for Worker overload (#2721)
|
||||
- Fix multiple tasks
|
||||
|
||||
SDK API changes ⚠️:
|
||||
- Increase targetSdkVersion to 30 (#2600)
|
||||
|
||||
Build 🧱:
|
||||
- Compile with Android SDK 30 (Android 11)
|
||||
|
||||
Other changes:
|
||||
- Update Dagger to 2.31 version so we can use the embedded AssistedInject feature
|
||||
|
||||
Changes in Element 1.0.14 (2020-01-15)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
- Enable url previews for notices (#2562)
|
||||
- Edit room permissions (#2471)
|
||||
|
||||
Improvements 🙌:
|
||||
- Add System theme option and set as default (#904, #2387)
|
||||
- Store megolm outbound session to improve send time of first message after app launch.
|
||||
- Warn user when they are leaving a not public room (#1460)
|
||||
- Option to disable emoji keyboard (#2563)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Unspecced msgType field in m.sticker (#2580)
|
||||
- Wait for all room members to be known before sending a message to a e2e room (#2518)
|
||||
- Url previews sometimes attached to wrong message (#2561)
|
||||
- Room Topic not displayed correctly after visiting a link (#2551)
|
||||
- Hiding membership events works the exact opposite (#2603)
|
||||
- Tapping drawer having more than 1 room in notifications gives "malformed link" error (#2605)
|
||||
- Sent image not displayed when opened immediately after sending (#409)
|
||||
- Initial sync is not retried correctly when there is some network error. (#2632)
|
||||
- Fix switch theme issue, and white field issue (#2599, #2528)
|
||||
- Fix request too large Uri error when joining a room
|
||||
|
||||
Translations 🗣:
|
||||
- New language supported: Hebrew
|
||||
|
||||
Build 🧱:
|
||||
- Remove dependency to org.greenrobot.eventbus library
|
||||
|
||||
Other changes:
|
||||
- Migrate to ViewBindings (#1072)
|
||||
|
||||
Changes in Element 1.0.13 (2020-12-18)
|
||||
===================================================
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix MSC2858 implementation details (#2540)
|
||||
|
||||
Changes in Element 1.0.12 (2020-12-15)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
- Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428)
|
||||
- Room setting: update join rules and guest access (#2442)
|
||||
- Url preview (#481)
|
||||
- Store encrypted file in cache and cleanup decrypted file at each app start (#2512)
|
||||
- Emoji Keyboard (#2520)
|
||||
- Social login (#2452)
|
||||
- Support for chat effects in timeline (confetti, snow) (#2535)
|
||||
|
||||
Improvements 🙌:
|
||||
- Add Setting Item to Change PIN (#2462)
|
||||
- Improve room history visibility setting UX (#1579)
|
||||
- Matrix.to deeplink custom scheme support
|
||||
- Homeserver history (#1933)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix cancellation of sending event (#2438)
|
||||
- Double bottomsheet effect after verify with passphrase
|
||||
- EditText cursor jumps to the start while typing fast (#2469)
|
||||
- UTD for events before invitation if member state events are hidden (#2486)
|
||||
- No known servers error is given when joining rooms on new Gitter bridge (#2516)
|
||||
- Show preview when sending attachment from the keyboard (#2440)
|
||||
- Do not compress GIFs (#1616, #1254)
|
||||
|
||||
SDK API changes ⚠️:
|
||||
- StateService now exposes suspendable function instead of using MatrixCallback.
|
||||
- RawCacheStrategy has been moved and renamed to CacheStrategy
|
||||
- FileService: remove useless FileService.DownloadMode
|
||||
|
||||
Build 🧱:
|
||||
- Upgrade some dependencies and Kotlin version
|
||||
- Use fragment-ktx and preference-ktx dependencies (fix lint issue KtxExtensionAvailable)
|
||||
- Upgrade Realm dependency to 10.1.2
|
||||
|
||||
Other changes:
|
||||
- Remove "Status.im" theme #2424
|
||||
- Log HTTP requests and responses in production (level BASIC, i.e. without any private data)
|
||||
|
||||
Changes in Element 1.0.11 (2020-11-27)
|
||||
===================================================
|
||||
|
||||
|
@@ -16,7 +16,6 @@
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
@@ -33,11 +32,11 @@ buildscript {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
@@ -55,6 +54,10 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -66,7 +69,6 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
|
@@ -17,19 +17,17 @@
|
||||
package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import im.vector.lib.attachmentviewer.databinding.ItemAnimatedImageAttachmentBinding
|
||||
|
||||
class AnimatedImageViewHolder constructor(itemView: View) :
|
||||
BaseViewHolder(itemView) {
|
||||
|
||||
val touchImageView: ImageView = itemView.findViewById(R.id.imageView)
|
||||
val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
|
||||
val views = ItemAnimatedImageAttachmentBinding.bind(itemView)
|
||||
|
||||
internal val target = DefaultImageLoaderTarget(this, this.touchImageView)
|
||||
internal val target = DefaultImageLoaderTarget(this, views.imageView)
|
||||
|
||||
override fun onRecycled() {
|
||||
super.onRecycled()
|
||||
touchImageView.setImageDrawable(null)
|
||||
views.imageView.setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
|
@@ -18,58 +18,70 @@
|
||||
package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowInsetsController
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import kotlinx.android.synthetic.main.activity_attachment_viewer.*
|
||||
import im.vector.lib.attachmentviewer.databinding.ActivityAttachmentViewerBinding
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.math.abs
|
||||
|
||||
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
|
||||
|
||||
lateinit var pager2: ViewPager2
|
||||
lateinit var imageTransitionView: ImageView
|
||||
lateinit var transitionImageContainer: ViewGroup
|
||||
protected val pager2: ViewPager2
|
||||
get() = views.attachmentPager
|
||||
protected val imageTransitionView: ImageView
|
||||
get() = views.transitionImageView
|
||||
protected val transitionImageContainer: ViewGroup
|
||||
get() = views.transitionImageContainer
|
||||
|
||||
var topInset = 0
|
||||
var bottomInset = 0
|
||||
var systemUiVisibility = true
|
||||
private var topInset = 0
|
||||
private var bottomInset = 0
|
||||
private var systemUiVisibility = true
|
||||
|
||||
private var overlayView: View? = null
|
||||
set(value) {
|
||||
if (value == overlayView) return
|
||||
overlayView?.let { rootContainer.removeView(it) }
|
||||
rootContainer.addView(value)
|
||||
overlayView?.let { views.rootContainer.removeView(it) }
|
||||
views.rootContainer.addView(value)
|
||||
value?.updatePadding(top = topInset, bottom = bottomInset)
|
||||
field = value
|
||||
}
|
||||
|
||||
private lateinit var views: ActivityAttachmentViewerBinding
|
||||
|
||||
private lateinit var swipeDismissHandler: SwipeToDismissHandler
|
||||
private lateinit var directionDetector: SwipeDirectionDetector
|
||||
private lateinit var scaleDetector: ScaleGestureDetector
|
||||
private lateinit var gestureDetector: GestureDetectorCompat
|
||||
|
||||
var currentPosition = 0
|
||||
private set
|
||||
|
||||
private var swipeDirection: SwipeDirection? = null
|
||||
|
||||
private fun isScaled() = attachmentsAdapter.isScaled(currentPosition)
|
||||
|
||||
private val attachmentsAdapter = AttachmentsAdapter()
|
||||
|
||||
private var wasScaled: Boolean = false
|
||||
private var isSwipeToDismissAllowed: Boolean = true
|
||||
private lateinit var attachmentsAdapter: AttachmentsAdapter
|
||||
private var isOverlayWasClicked = false
|
||||
|
||||
// private val shouldDismissToBottom: Boolean
|
||||
@@ -86,26 +98,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// This is important for the dispatchTouchEvent, if not we must correct
|
||||
// the touch coordinates
|
||||
window.decorView.systemUiVisibility = (
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
setDecorViewFullScreen()
|
||||
|
||||
setContentView(R.layout.activity_attachment_viewer)
|
||||
attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
attachmentsAdapter = AttachmentsAdapter()
|
||||
attachmentPager.adapter = attachmentsAdapter
|
||||
imageTransitionView = transitionImageView
|
||||
transitionImageContainer = findViewById(R.id.transitionImageContainer)
|
||||
pager2 = attachmentPager
|
||||
views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
|
||||
setContentView(views.root)
|
||||
views.attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
|
||||
views.attachmentPager.adapter = attachmentsAdapter
|
||||
directionDetector = createSwipeDirectionDetector()
|
||||
gestureDetector = createGestureDetector()
|
||||
|
||||
attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
views.attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
isImagePagerIdle = state == ViewPager2.SCROLL_STATE_IDLE
|
||||
}
|
||||
@@ -116,12 +118,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
})
|
||||
|
||||
swipeDismissHandler = createSwipeToDismissHandler()
|
||||
rootContainer.setOnTouchListener(swipeDismissHandler)
|
||||
rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = dismissContainer.height / 4 }
|
||||
views.rootContainer.setOnTouchListener(swipeDismissHandler)
|
||||
views.rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = views.dismissContainer.height / 4 }
|
||||
|
||||
scaleDetector = createScaleGestureDetector()
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(rootContainer) { _, insets ->
|
||||
ViewCompat.setOnApplyWindowInsetsListener(views.rootContainer) { _, insets ->
|
||||
overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom)
|
||||
topInset = insets.systemWindowInsetTop
|
||||
bottomInset = insets.systemWindowInsetBottom
|
||||
@@ -129,6 +131,29 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun setDecorViewFullScreen() {
|
||||
// This is important for the dispatchTouchEvent, if not we must correct
|
||||
// the touch coordinates
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
|
||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
// new API instead of FLAG_TRANSLUCENT_NAVIGATION
|
||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
} else {
|
||||
window.decorView.systemUiVisibility = (
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSelectedPositionChanged(position: Int) {
|
||||
attachmentsAdapter.recyclerView?.findViewHolderForAdapterPosition(currentPosition)?.let {
|
||||
(it as? BaseViewHolder)?.onSelected(false)
|
||||
@@ -170,7 +195,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
if (swipeDirection == null && (scaleDetector.isInProgress || ev.pointerCount > 1 || wasScaled)) {
|
||||
wasScaled = true
|
||||
// Log.v("ATTACHEMENTS", "dispatch to pager")
|
||||
return attachmentPager.dispatchTouchEvent(ev)
|
||||
return views.attachmentPager.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
// Log.v("ATTACHEMENTS", "is current item scaled ${isScaled()}")
|
||||
@@ -196,16 +221,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
private fun handleEventActionDown(event: MotionEvent) {
|
||||
swipeDirection = null
|
||||
wasScaled = false
|
||||
attachmentPager.dispatchTouchEvent(event)
|
||||
views.attachmentPager.dispatchTouchEvent(event)
|
||||
|
||||
swipeDismissHandler.onTouch(rootContainer, event)
|
||||
swipeDismissHandler.onTouch(views.rootContainer, event)
|
||||
isOverlayWasClicked = dispatchOverlayTouch(event)
|
||||
}
|
||||
|
||||
private fun handleEventActionUp(event: MotionEvent) {
|
||||
// wasDoubleTapped = false
|
||||
swipeDismissHandler.onTouch(rootContainer, event)
|
||||
attachmentPager.dispatchTouchEvent(event)
|
||||
swipeDismissHandler.onTouch(views.rootContainer, event)
|
||||
views.attachmentPager.dispatchTouchEvent(event)
|
||||
isOverlayWasClicked = dispatchOverlayTouch(event)
|
||||
}
|
||||
|
||||
@@ -220,12 +245,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
private fun toggleOverlayViewVisibility() {
|
||||
if (systemUiVisibility) {
|
||||
// we hide
|
||||
TransitionManager.beginDelayedTransition(rootContainer)
|
||||
TransitionManager.beginDelayedTransition(views.rootContainer)
|
||||
hideSystemUI()
|
||||
overlayView?.isVisible = false
|
||||
} else {
|
||||
// we show
|
||||
TransitionManager.beginDelayedTransition(rootContainer)
|
||||
TransitionManager.beginDelayedTransition(views.rootContainer)
|
||||
showSystemUI()
|
||||
overlayView?.isVisible = true
|
||||
}
|
||||
@@ -238,11 +263,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
return when (swipeDirection) {
|
||||
SwipeDirection.Up, SwipeDirection.Down -> {
|
||||
if (isSwipeToDismissAllowed && !wasScaled && isImagePagerIdle) {
|
||||
swipeDismissHandler.onTouch(rootContainer, event)
|
||||
swipeDismissHandler.onTouch(views.rootContainer, event)
|
||||
} else true
|
||||
}
|
||||
SwipeDirection.Left, SwipeDirection.Right -> {
|
||||
attachmentPager.dispatchTouchEvent(event)
|
||||
views.attachmentPager.dispatchTouchEvent(event)
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
@@ -250,8 +275,8 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
|
||||
private fun handleSwipeViewMove(translationY: Float, translationLimit: Int) {
|
||||
val alpha = calculateTranslationAlpha(translationY, translationLimit)
|
||||
backgroundView.alpha = alpha
|
||||
dismissContainer.alpha = alpha
|
||||
views.backgroundView.alpha = alpha
|
||||
views.dismissContainer.alpha = alpha
|
||||
overlayView?.alpha = alpha
|
||||
}
|
||||
|
||||
@@ -265,7 +290,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
|
||||
private fun createSwipeToDismissHandler()
|
||||
: SwipeToDismissHandler = SwipeToDismissHandler(
|
||||
swipeView = dismissContainer,
|
||||
swipeView = views.dismissContainer,
|
||||
shouldAnimateDismiss = { shouldAnimateDismiss() },
|
||||
onDismiss = { animateClose() },
|
||||
onSwipeViewMove = ::handleSwipeViewMove)
|
||||
@@ -308,28 +333,48 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
||||
?.handleCommand(commands)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun hideSystemUI() {
|
||||
systemUiVisibility = false
|
||||
// Enables regular immersive mode.
|
||||
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
|
||||
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||
// Set the content to appear under the system bars so that the
|
||||
// content doesn't resize when the system bars hide and show.
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
// Hide the nav bar and status bar
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_FULLSCREEN)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
// new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
|
||||
// New API instead of SYSTEM_UI_FLAG_IMMERSIVE
|
||||
window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
|
||||
// New API instead of FLAG_TRANSLUCENT_STATUS
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
// New API instead of FLAG_TRANSLUCENT_NAVIGATION
|
||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar)
|
||||
} else {
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||
// Set the content to appear under the system bars so that the
|
||||
// content doesn't resize when the system bars hide and show.
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
// Hide the nav bar and status bar
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_FULLSCREEN)
|
||||
}
|
||||
}
|
||||
|
||||
// Shows the system bars by removing all the flags
|
||||
// except for the ones that make the content appear under the system bars.
|
||||
@Suppress("DEPRECATION")
|
||||
private fun showSystemUI() {
|
||||
systemUiVisibility = true
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
} else {
|
||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ class AttachmentsAdapter : RecyclerView.Adapter<BaseViewHolder>() {
|
||||
fun isScaled(position: Int): Boolean {
|
||||
val holder = recyclerView?.findViewHolderForAdapterPosition(position)
|
||||
if (holder is ZoomableImageViewHolder) {
|
||||
return holder.touchImageView.attacher.scale > 1f
|
||||
return holder.views.touchImageView.attacher.scale > 1f
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@@ -44,29 +44,29 @@ internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, pri
|
||||
|
||||
override fun onResourceLoading(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = true
|
||||
holder.views.imageLoaderProgress.isVisible = true
|
||||
}
|
||||
|
||||
override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.touchImageView.setImageDrawable(errorDrawable)
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
holder.views.imageView.setImageDrawable(errorDrawable)
|
||||
}
|
||||
|
||||
override fun onResourceCleared(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.touchImageView.setImageDrawable(placeholder)
|
||||
holder.views.imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onResourceReady(uid: String, resource: Drawable) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
// Glide mess up the view size :/
|
||||
holder.touchImageView.updateLayoutParams {
|
||||
holder.views.imageView.updateLayoutParams {
|
||||
width = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
height = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
holder.touchImageView.setImageDrawable(resource)
|
||||
holder.views.imageView.setImageDrawable(resource)
|
||||
if (resource is Animatable) {
|
||||
resource.start()
|
||||
}
|
||||
@@ -77,30 +77,30 @@ internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, pri
|
||||
|
||||
override fun onResourceLoading(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = true
|
||||
holder.touchImageView.setImageDrawable(placeholder)
|
||||
holder.views.imageLoaderProgress.isVisible = true
|
||||
holder.views.touchImageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.touchImageView.setImageDrawable(errorDrawable)
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
holder.views.touchImageView.setImageDrawable(errorDrawable)
|
||||
}
|
||||
|
||||
override fun onResourceCleared(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.touchImageView.setImageDrawable(placeholder)
|
||||
holder.views.touchImageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onResourceReady(uid: String, resource: Drawable) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.imageLoaderProgress.isVisible = false
|
||||
holder.views.imageLoaderProgress.isVisible = false
|
||||
// Glide mess up the view size :/
|
||||
holder.touchImageView.updateLayoutParams {
|
||||
holder.views.touchImageView.updateLayoutParams {
|
||||
width = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
height = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
holder.touchImageView.setImageDrawable(resource)
|
||||
holder.views.touchImageView.setImageDrawable(resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -49,19 +49,19 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
|
||||
|
||||
override fun onThumbnailResourceCleared(uid: String, placeholder: Drawable?) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.thumbnailImage.setImageDrawable(placeholder)
|
||||
holder.views.videoThumbnailImage.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onThumbnailResourceReady(uid: String, resource: Drawable) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.thumbnailImage.setImageDrawable(resource)
|
||||
holder.views.videoThumbnailImage.setImageDrawable(resource)
|
||||
}
|
||||
|
||||
override fun onVideoFileLoading(uid: String) {
|
||||
if (holder.boundResourceUid != uid) return
|
||||
holder.thumbnailImage.isVisible = true
|
||||
holder.loaderProgressBar.isVisible = true
|
||||
holder.videoView.isVisible = false
|
||||
holder.views.videoThumbnailImage.isVisible = true
|
||||
holder.views.videoLoaderProgress.isVisible = true
|
||||
holder.views.videoView.isVisible = false
|
||||
}
|
||||
|
||||
override fun onVideoFileLoadFailed(uid: String) {
|
||||
@@ -82,8 +82,8 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
|
||||
}
|
||||
|
||||
private fun arrangeForVideoReady() {
|
||||
holder.thumbnailImage.isVisible = false
|
||||
holder.loaderProgressBar.isVisible = false
|
||||
holder.videoView.isVisible = true
|
||||
holder.views.videoThumbnailImage.isVisible = false
|
||||
holder.views.videoLoaderProgress.isVisible = false
|
||||
holder.views.videoView.isVisible = true
|
||||
}
|
||||
}
|
||||
|
@@ -18,11 +18,8 @@ package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.VideoView
|
||||
import androidx.core.view.isVisible
|
||||
import im.vector.lib.attachmentviewer.databinding.ItemVideoAttachmentBinding
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
@@ -44,13 +41,9 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
|
||||
var eventListener: WeakReference<AttachmentEventListener>? = null
|
||||
|
||||
val thumbnailImage: ImageView = itemView.findViewById(R.id.videoThumbnailImage)
|
||||
val videoView: VideoView = itemView.findViewById(R.id.videoView)
|
||||
val loaderProgressBar: ProgressBar = itemView.findViewById(R.id.videoLoaderProgress)
|
||||
val videoControlIcon: ImageView = itemView.findViewById(R.id.videoControlIcon)
|
||||
val errorTextView: TextView = itemView.findViewById(R.id.videoMediaViewerErrorView)
|
||||
val views = ItemVideoAttachmentBinding.bind(itemView)
|
||||
|
||||
internal val target = DefaultVideoLoaderTarget(this, thumbnailImage)
|
||||
internal val target = DefaultVideoLoaderTarget(this, views.videoThumbnailImage)
|
||||
|
||||
override fun onRecycled() {
|
||||
super.onRecycled()
|
||||
@@ -77,12 +70,12 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
}
|
||||
|
||||
override fun entersBackground() {
|
||||
if (videoView.isPlaying) {
|
||||
progress = videoView.currentPosition
|
||||
if (views.videoView.isPlaying) {
|
||||
progress = views.videoView.currentPosition
|
||||
progressDisposable?.dispose()
|
||||
progressDisposable = null
|
||||
videoView.stopPlayback()
|
||||
videoView.pause()
|
||||
views.videoView.stopPlayback()
|
||||
views.videoView.pause()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +85,9 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
|
||||
override fun onSelected(selected: Boolean) {
|
||||
if (!selected) {
|
||||
if (videoView.isPlaying) {
|
||||
progress = videoView.currentPosition
|
||||
videoView.stopPlayback()
|
||||
if (views.videoView.isPlaying) {
|
||||
progress = views.videoView.currentPosition
|
||||
views.videoView.stopPlayback()
|
||||
} else {
|
||||
progress = 0
|
||||
}
|
||||
@@ -109,34 +102,34 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
}
|
||||
|
||||
private fun startPlaying() {
|
||||
thumbnailImage.isVisible = false
|
||||
loaderProgressBar.isVisible = false
|
||||
videoView.isVisible = true
|
||||
views.videoThumbnailImage.isVisible = false
|
||||
views.videoLoaderProgress.isVisible = false
|
||||
views.videoView.isVisible = true
|
||||
|
||||
videoView.setOnPreparedListener {
|
||||
views.videoView.setOnPreparedListener {
|
||||
progressDisposable?.dispose()
|
||||
progressDisposable = Observable.interval(100, TimeUnit.MILLISECONDS)
|
||||
.timeInterval()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
val duration = videoView.duration
|
||||
val progress = videoView.currentPosition
|
||||
val isPlaying = videoView.isPlaying
|
||||
val duration = views.videoView.duration
|
||||
val progress = views.videoView.currentPosition
|
||||
val isPlaying = views.videoView.isPlaying
|
||||
// Log.v("FOO", "isPlaying $isPlaying $progress/$duration")
|
||||
eventListener?.get()?.onEvent(AttachmentEvents.VideoEvent(isPlaying, progress, duration))
|
||||
}
|
||||
}
|
||||
try {
|
||||
videoView.setVideoPath(mVideoPath)
|
||||
views.videoView.setVideoPath(mVideoPath)
|
||||
} catch (failure: Throwable) {
|
||||
// Couldn't open
|
||||
Log.v(VideoViewHolder::class.java.name, "Failed to start video")
|
||||
}
|
||||
|
||||
if (!wasPaused) {
|
||||
videoView.start()
|
||||
views.videoView.start()
|
||||
if (progress > 0) {
|
||||
videoView.seekTo(progress)
|
||||
views.videoView.seekTo(progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,17 +139,17 @@ class VideoViewHolder constructor(itemView: View) :
|
||||
when (commands) {
|
||||
AttachmentCommands.StartVideo -> {
|
||||
wasPaused = false
|
||||
videoView.start()
|
||||
views.videoView.start()
|
||||
}
|
||||
AttachmentCommands.PauseVideo -> {
|
||||
wasPaused = true
|
||||
videoView.pause()
|
||||
views.videoView.pause()
|
||||
}
|
||||
is AttachmentCommands.SeekTo -> {
|
||||
val duration = videoView.duration
|
||||
val duration = views.videoView.duration
|
||||
if (duration > 0) {
|
||||
val seekDuration = duration * (commands.percentProgress / 100f)
|
||||
videoView.seekTo(seekDuration.toInt())
|
||||
views.videoView.seekTo(seekDuration.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,31 +17,29 @@
|
||||
package im.vector.lib.attachmentviewer
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import com.github.chrisbanes.photoview.PhotoView
|
||||
import im.vector.lib.attachmentviewer.databinding.ItemImageAttachmentBinding
|
||||
|
||||
class ZoomableImageViewHolder constructor(itemView: View) :
|
||||
BaseViewHolder(itemView) {
|
||||
|
||||
val touchImageView: PhotoView = itemView.findViewById(R.id.touchImageView)
|
||||
val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
|
||||
val views = ItemImageAttachmentBinding.bind(itemView)
|
||||
|
||||
init {
|
||||
touchImageView.setAllowParentInterceptOnEdge(false)
|
||||
touchImageView.setOnScaleChangeListener { scaleFactor, _, _ ->
|
||||
views.touchImageView.setAllowParentInterceptOnEdge(false)
|
||||
views.touchImageView.setOnScaleChangeListener { scaleFactor, _, _ ->
|
||||
// Log.v("ATTACHEMENTS", "scaleFactor $scaleFactor")
|
||||
// It's a bit annoying but when you pitch down the scaling
|
||||
// is not exactly one :/
|
||||
touchImageView.setAllowParentInterceptOnEdge(scaleFactor <= 1.0008f)
|
||||
views.touchImageView.setAllowParentInterceptOnEdge(scaleFactor <= 1.0008f)
|
||||
}
|
||||
touchImageView.setScale(1.0f, true)
|
||||
touchImageView.setAllowParentInterceptOnEdge(true)
|
||||
views.touchImageView.setScale(1.0f, true)
|
||||
views.touchImageView.setAllowParentInterceptOnEdge(true)
|
||||
}
|
||||
|
||||
internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, touchImageView)
|
||||
internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, views.touchImageView)
|
||||
|
||||
override fun onRecycled() {
|
||||
super.onRecycled()
|
||||
touchImageView.setImageDrawable(null)
|
||||
views.touchImageView.setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
|
6
attachment-viewer/src/main/res/values/colors.xml
Normal file
6
attachment-viewer/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
|
||||
<color name="half_transparent_status_bar">#80000000</color>
|
||||
|
||||
</resources>
|
10
build.gradle
10
build.gradle
@@ -2,8 +2,8 @@
|
||||
|
||||
buildscript {
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
ext.kotlin_version = '1.4.10'
|
||||
ext.kotlin_coroutines_version = "1.3.9"
|
||||
ext.kotlin_version = '1.4.20'
|
||||
ext.kotlin_coroutines_version = "1.4.1"
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
@@ -12,7 +12,7 @@ buildscript {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
classpath 'com.google.gms:google-services:4.3.4'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
@@ -43,6 +43,10 @@ allprojects {
|
||||
includeGroupByRegex 'com\\.github\\.chrisbanes'
|
||||
// PFLockScreen-Android
|
||||
includeGroupByRegex 'com\\.github\\.vector-im'
|
||||
|
||||
//Chat effects
|
||||
includeGroupByRegex 'com\\.github\\.jetradarmobile'
|
||||
includeGroupByRegex 'nl\\.dionsegijn'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
|
@@ -165,7 +165,7 @@ In this case, the user can click on "Sign in with SSO" and the native web browse
|
||||
|
||||
> https://homeserver.with.sso/_matrix/client/r0/login/sso/redirect?redirectUrl=element%3A%2F%element
|
||||
|
||||
The parameter `redirectUrl` is set to `element://element`.
|
||||
The parameter `redirectUrl` is set to `element://connect`.
|
||||
|
||||
ChromeCustomTabs are an intermediate way to display a WebPage, between a WebView and using the external browser. More info can be found [here](https://developer.chrome.com/multidevice/android/customtabs)
|
||||
|
||||
@@ -175,7 +175,7 @@ During the process, user may be asked to validate an email by clicking on a link
|
||||
|
||||
Once the process is finished, the web page will call the `redirectUrl` with an extra parameter `loginToken`
|
||||
|
||||
> element://element?loginToken=MDAxOWxvY2F0aW9uIG1vemlsbGEub3JnCjAwMTNpZGVudGlmaWVy
|
||||
> element://connect?loginToken=MDAxOWxvY2F0aW9uIG1vemlsbGEub3JnCjAwMTNpZGVudGlmaWVy
|
||||
|
||||
This navigation is intercepted by Element by the `LoginActivity`, which will then ask the homeserver to convert this `loginToken` to an access token
|
||||
|
||||
|
@@ -27,7 +27,6 @@ $ source env/bin/activate
|
||||
Every time you want to launch these test homeservers, type:
|
||||
|
||||
```shell script
|
||||
$ virtualenv -p python3 env
|
||||
$ source env/bin/activate
|
||||
(env) $ demo/start.sh --no-rate-limit
|
||||
```
|
||||
|
@@ -1 +1,2 @@
|
||||
// TODO
|
||||
Diese neue Version enthält hauptsächlich Fehlerkorrekturen und Verbesserungen. Nachrichten verschicken geht jetzt viel schneller.
|
||||
Vollständige Versionshinweise: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
2
fastlane/metadata/android/de/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/de/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Diese neue Version enthält hauptsächlich Verbesserungen der Benutzer*innenoberfläche und der Handhabung. Du kannst jetzt ganz schnell Freund*innen einladen und DMs erstellen, indem du schlicht einen QR-Code scannst.
|
||||
Vollständige Versionshinweise: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/en-US/changelogs/40100120.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100120.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: URL Preview, new Emoji keyboard, new room settings capabilities, and snow for Christmas!
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.12
|
2
fastlane/metadata/android/en-US/changelogs/40100130.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100130.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: URL Preview, new Emoji keyboard, new room settings capabilities, and snow for Christmas!
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.13
|
2
fastlane/metadata/android/en-US/changelogs/40100140.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100140.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: Edit room permissions, automatic light/dark theme, and a bunch of bug fixes.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.14
|
2
fastlane/metadata/android/en-US/changelogs/40100150.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100150.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: Social Login support.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.15
|
2
fastlane/metadata/android/en-US/changelogs/40100160.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100160.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Main changes in this version: Social Login support.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.15 and https://github.com/vector-im/element-android/releases/tag/v1.0.16
|
2
fastlane/metadata/android/et/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Selles uues versioonis leidub põhiliselt veaparandusi ja pisikohendusi. Sõnumite saatmine on nüüd märkatavalt kiirem.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/et/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Uues versioonis leidub põhiliselt kasutajaliidese ning kasutajakogemuse parandusi. Nüüd saad sõpradele kutseid saata ning otsevestlusi alustada QR-koodi lugemise abil.
|
||||
Kõik muudatused: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -1 +1,2 @@
|
||||
// برای انجام
|
||||
این نگارش جدید به طور عمده شامل رفع اشکالها و بهبودها است. ارسال پیام اکنون بسیار سریعتر است.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
2
fastlane/metadata/android/fa/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/fa/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
این نگارش جدید به طور عمده شامل رابط کاربری و بهبود تجربه کاربر است. اکنون میتوانید با پویش کدهای QR دوستانتان را دعوت کرده و بسیار سریع پیام مستقیم ایجاد کنید.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -1 +1 @@
|
||||
گپ و تماس نامتمرکز امن. دادههایتان را از شرکتها امن نگه دارید.
|
||||
گپ و تماس نامتمرکز امن. دادههایتان را از اشخاص سوم امن نگه دارید.
|
||||
|
@@ -1 +1 @@
|
||||
المنت (ریوت سابق)
|
||||
Element (پیشتر Riot.im)
|
||||
|
2
fastlane/metadata/android/fi/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/fi/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Tämä versio sisältää virheenkorjauksia ja muita parannuksia. Viestien lähettäminen on nyt paljon nopeampaa.
|
||||
Täysi muutosloki: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/fi/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/fi/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Tämä versio sisältää pääosin käyttöliittymä- ja käyttökokemusparannuksia. Voit nyt kutsua kavereita ja luoda yksityisviestejä nopeasti QR-koodeja lukemalla.
|
||||
Täysi muutosloki: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
30
fastlane/metadata/android/fi/full_description.txt
Normal file
30
fastlane/metadata/android/fi/full_description.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
Element on uudenlainen viestinsovellus, joka:
|
||||
|
||||
1. Antaa sinun päättää yksityisyydestäsi
|
||||
2. Antaa sinun kommunikoida kenen tahansa kanssa Matrix-verkossa ja jopa sen ulkopuolella siltaamalla sovelluksiin, kuten Slack
|
||||
3. Suojaa sinua mainonnalta, tietojen keräämiseltä ja suljetuilta alustoilta
|
||||
4. Suojaa sinut päästä päähän -salauksella sekä ristiin varmentamisella muiden todentamiseksi
|
||||
|
||||
Element eroaa täysin muista viestintäsovelluksista, koska se on hajautettu ja avointa lähdekoodia.
|
||||
|
||||
Element antaa sinun isännöidä itse - tai valita palveluntarjoajan - jotta sinulla on yksityisyys ja voit hallita tietojasi sekä keskustelujasi. Se antaa sinulle pääsyn avoimeen verkkoon, joten et jää juttelemaan vain toisten Elementin käyttäjien kanssa. Se on myös hyvin turvallinen.
|
||||
|
||||
Element pystyy tekemään kaiken tämän, koska se toimii Matrixilla - avoimella, hajautetun viestinnän standardilla.
|
||||
|
||||
Element antaa sinulle päätösvallan antamalla sinun valita, kuka isännöi keskustelujasi. Element-sovelluksessa voit valita isännän eri tavoin:
|
||||
|
||||
1. Hanki ilmainen tili Matrix-kehittäjien ylläpitämällä matrix.org-palvelimella tai valitse tuhansista vapaaehtoisten ylläpitämistä julkisista palvelimista.
|
||||
2. Isännöi tiliäsi itse ylläpitämällä palvelinta omalla laitteellasi
|
||||
3. Luo tili sinua varten tehdyllä palvelimella tilaamalla Element Matrix Services -palvelu
|
||||
|
||||
<b>Miksi valita Element?</b>
|
||||
|
||||
<b>OMAT TIEDOT</b>: Sinä päätät, missä tietosi ja viestisi säilytetään. Sinä määräät, ei jokin jättiyhtiö, joka tutkii tietojasi tai antaa niitä kolmansille osapuolille.
|
||||
|
||||
<b>AVOINTA VIESTINTÄÄ JA YHTEISTYÖTÄ</b>: Voit keskustella kaikkien muiden Matrix-verkon käyttäjien kanssa, riippumatta siitä käyttävätkö he Elementiä tai muuta Matrix-sovellusta, ja vaikka he käyttäisivät eri viestijärjestelmiä, kuten Slack, IRC tai XMPP.
|
||||
|
||||
<b>ERITTÄIN TURVALLINEN</b>: Vahva päästä päähän -salaus (vain keskustelussa olevat voivat purkaa viestien salauksen), ja ristiin varmentaminen keskustelun osallistujien laitteiden tarkistamiseksi.
|
||||
|
||||
<b>KATTAVAA VIESTINTÄÄ</b>: Viestit, ääni- ja videopuhelut, tiedostojen jakaminen, näytön jakaminen ja koko joukko integraatioita, botteja ja sovelmia. Rakenna huoneita ja yhteisöjä, pidä yhteyttä ja hoida asiasi.
|
||||
|
||||
<b>MISSÄ TAHANSA OLETKIN</b>: Pidä yhteyttä missä tahansa, täysin synkronoidun viestihistorian kautta kaikilla laitteillasi ja verkossa osoitteessa https://app.element.io.
|
2
fastlane/metadata/android/fr/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/fr/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Cette nouvelle version contient principalement des corrections de bogues et des améliorations. Envoyer un message est maintenant plus rapide.
|
||||
Liste complète des changements : https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/hu/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/hu/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Ez az új verzió főképp hibajavításokat, és teljesítménybeli fejlesztéseket tartalmaz. Most már sokkal gyorsabb az üzenetek elküldése.
|
||||
A változtatások teljes listája itt található: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/hu/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/hu/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Ez az új verzió főleg a felhasználói felülettel és a felhasználói élménnyel kapcsolatos javításokat tartalmaz. Mostantól már sokkal gyorsabban hívhatsz meg új ismerősöket a QR kód beolvasás által.
|
||||
A változtatások teljes listája itt található: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -17,7 +17,7 @@ Az Element a te kezedbe adja az irányítást azáltal, hogy eldöntheted, ki t
|
||||
2. A saját számítógépeden is futtathatsz szervert
|
||||
3. Előfizethetsz egy saját szerverre az Element Matrix Szolgáltatások platformon
|
||||
|
||||
<b>Miért válaszd az Element-et?</b>
|
||||
<b>Miért jó az Element-et választani?</b>
|
||||
|
||||
<b>ADATAID MEGVÉDÉSE</b>: Eldöntheted, hol tárold az adataid és üzeneteid. A te tulajdonodban van, nem valami megacégnél, ami bányássza az adataid, vagy továbbadja másoknak.
|
||||
|
||||
|
@@ -1 +1,2 @@
|
||||
// DA FARE
|
||||
Questa nuova versione contiene soprattutto correzioni di errori e miglioramenti. L'invio di messaggi ora è molto più veloce.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
2
fastlane/metadata/android/it/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/it/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Questa nuova versione contiene principalmente miglioramenti di interfaccia ed esperienza utente. Ora puoi invitare amici e iniziare messaggi diretti rapidamente tramite codici QR.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/nb_NO/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/nb_NO/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Denne nye versjonen inneholder hovedsakelig feilrettinger og forbedringer. Å sende en melding er nå mye raskere.
|
||||
Full endringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/nb_NO/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/nb_NO/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Denne nye versjonen inneholder hovedsakelig forbedringer av brukergrensesnittet og brukeropplevelsen. Nå kan du invitere venner og opprette DM veldig raskt ved å skanne QR-koder.
|
||||
Full endringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
1
fastlane/metadata/android/nb_NO/short_description.txt
Normal file
1
fastlane/metadata/android/nb_NO/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Sikker desentralisert chat & VoIP. Beskytt dataene dine fra tredjeparter.
|
1
fastlane/metadata/android/nb_NO/title.txt
Normal file
1
fastlane/metadata/android/nb_NO/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Element (tidligere Riot.im)
|
@@ -1 +1,2 @@
|
||||
// A FAZER
|
||||
Esta nova versão contém principalmente correções de erros e melhorias. Enviar mensagens agora é muito mais rápido.
|
||||
Registro de todas as alterações: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
2
fastlane/metadata/android/pt_BR/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/pt_BR/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Esta nova versão contém principalmente melhorias na interface do usuário e na experiência do usuário. Agora você pode convidar amigos e criar conversas rapidamente, digitalizando códigos QR.
|
||||
Registro completo de alterações: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/ru/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/ru/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Эта новая версия в основном содержит исправления ошибок и улучшения. Отправка сообщения стала намного быстрее.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/ru/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/ru/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Эта новая версия в основном содержит улучшения пользовательского интерфейса и взаимодействия с пользователем. Теперь вы можете приглашать друзей и очень быстро создавать чаты, сканируя QR-коды.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/sk/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Táto verzia obsahuje predovšetkým opravy chýb. Odosielanie správ je odteraz omnoho rýchlejšie.
|
||||
Kompletný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/sk/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Táto verzia obsahuje najmä vylepšenia používateľského rozhrania. Pozývať priateľov alebo vytvárať priame konverzácie môžete veľmi rýchlo naskenovaním QR kódov.
|
||||
Kompletný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -1 +1,2 @@
|
||||
// ATT GÖRA
|
||||
Den här nya versionen innehåller mest buggfixar och förbättringar. Det går nu mycket snabbare att skicka meddelanden.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
2
fastlane/metadata/android/sv/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/sv/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Den här nya versionen innehåller mest förbättringar för användargränssnittet och användarupplevelsen. Du kan nu bjuda in vänner och skapa direktmeddelanden väldigt snabbt genom att skanna QR-koder.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/uk/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Ця версія містить переважно виправлення помилок та деякі покращення. Відправлення повідомлень стало тепер ще швидшим.
|
||||
Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/uk/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Ця нова версія містить переважно поліпшення інтерфейсу та зручності користування. Тепер ви можете запросити друзів і створити прямі повідомлення дуже швидко, скануючи QR-коди.
|
||||
Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -7,7 +7,7 @@ Element — це застосунок для спілкування та спі
|
||||
|
||||
Element ґрунтовно відрізняється від інших застосунків для спілкування та співпраці тому що він є децентралізованим та відкритоджерельним.
|
||||
|
||||
Element дозволяє вам розміщувати сервер в себе або обирати будь-якого з надавачів послуг, таким чином забезпечуючи вам конфіденційність і можливість володіти власними даними й бесідами та контролювати їх. Він надає вам доступ до відкритої мережі, тож ви не є обмеженими спілкуванням виключно з користувачами Element. І він є дуже надійним та безпечним.
|
||||
Element дозволяє вам розміщувати сервер в себе або обирати будь-якого з надавачів послуг, таким чином забезпечуючи вам конфіденційність і можливість володіти власними даними й бесідами та контролювати їх. Він надає вам доступ до відкритої мережі, тож ви не є обмеженими спілкуванням виключно з користувачами Element. І він є дуже надійним та безпечним.
|
||||
|
||||
Element здатен забезпечити усе це завдяки тому, що він заснований на протоколі Matrix — стандарті для відкритого та децентралізованого спілкування.
|
||||
|
||||
|
2
fastlane/metadata/android/vi/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/vi/changelogs/40100100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Phiên bản mới này chủ yếu bao gồm sửa lỗi và một số cải thiện. Gửi tin nhắn trở nên nhanh chóng hơn trước.
|
||||
Danh sách đầy đủ các thay đổi: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/vi/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/vi/changelogs/40100110.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Phiên bản mới này chủ yếu bao gồm các cải thiện về giao diện và trải nghiệm người dùng. Bây giờ bạn có thể mời bạn bè và bắt đầu nói chuyện nhanh chóng bằng cách quét mã QR.
|
||||
Danh sách đầy đủ các thay đổi: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
1
fastlane/metadata/android/vi/short_description.txt
Normal file
1
fastlane/metadata/android/vi/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Ứng dụng chat và gọi phân tán bảo mật. Bảo vệ dữ liệu của bạn khỏi bên thứ ba.
|
1
fastlane/metadata/android/vi/title.txt
Normal file
1
fastlane/metadata/android/vi/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Element (trước là Riot.im)
|
@@ -1 +1,2 @@
|
||||
// 待辦事項
|
||||
這個新版本主要包含錯誤修復與改善。傳送訊息更快了。
|
||||
完整的變更紀錄請見:https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
||||
|
@@ -0,0 +1,2 @@
|
||||
這個新版本主要包含使用者介面與使用者體驗改善。現在您可以邀請朋友,並透過掃描 QR code 來快速建立直接訊息了。
|
||||
完整變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
@@ -0,0 +1,2 @@
|
||||
此版本中的主要變更:URL 預覽、新的表情符號鍵盤、新的聊天室設定功能以及聖誕節降雪!
|
||||
完整變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.0.12
|
@@ -0,0 +1,2 @@
|
||||
此版本中的主要變更:URL 預覽、新的表情符號鍵盤、新的聊天室設定功能以及聖誕節降雪!
|
||||
完整變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.0.12
|
@@ -18,7 +18,7 @@ org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.vfs.watch=true
|
||||
|
||||
vector.debugPrivateData=false
|
||||
vector.httpLogLevel=NONE
|
||||
vector.httpLogLevel=BASIC
|
||||
|
||||
# Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above
|
||||
#vector.debugPrivateData=true
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=22449f5231796abd892c98b2a07c9ceebe4688d192cd2d6763f8e3bf8acbedeb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionSha256Sum=3db89524a3981819ff28c3f979236c1274a726e146ced0c8a2020417f9bc0782
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
@@ -36,9 +36,10 @@ android {
|
||||
dependencies {
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlin_coroutines_version"
|
||||
|
||||
// Paging
|
||||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
|
||||
|
@@ -21,34 +21,36 @@ import org.matrix.android.sdk.api.util.Cancelable
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
|
||||
fun <T> singleBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Single<T> = Single.create {
|
||||
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
|
||||
fun <T> singleBuilder(builder: (MatrixCallback<T>) -> Cancelable): Single<T> = Single.create { emitter ->
|
||||
val callback = object : MatrixCallback<T> {
|
||||
override fun onSuccess(data: T) {
|
||||
it.onSuccess(data)
|
||||
// Add `!!` to fix the warning:
|
||||
// "Type mismatch: type parameter with nullable bounds is used T is used where T was expected. This warning will become an error soon"
|
||||
emitter.onSuccess(data!!)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
it.tryOnError(failure)
|
||||
emitter.tryOnError(failure)
|
||||
}
|
||||
}
|
||||
val cancelable = builder(callback)
|
||||
it.setCancellable {
|
||||
emitter.setCancellable {
|
||||
cancelable.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> completableBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Completable = Completable.create {
|
||||
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
|
||||
fun <T> completableBuilder(builder: (MatrixCallback<T>) -> Cancelable): Completable = Completable.create { emitter ->
|
||||
val callback = object : MatrixCallback<T> {
|
||||
override fun onSuccess(data: T) {
|
||||
it.onComplete()
|
||||
emitter.onComplete()
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
it.tryOnError(failure)
|
||||
emitter.tryOnError(failure)
|
||||
}
|
||||
}
|
||||
val cancelable = builder(callback)
|
||||
it.setCancellable {
|
||||
emitter.setCancellable {
|
||||
cancelable.cancel()
|
||||
}
|
||||
}
|
||||
|
@@ -17,14 +17,20 @@
|
||||
package org.matrix.android.sdk.rx
|
||||
|
||||
import android.net.Uri
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import kotlinx.coroutines.rx2.rxCompletable
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||
@@ -32,9 +38,6 @@ import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
class RxRoom(private val room: Room) {
|
||||
|
||||
@@ -119,32 +122,28 @@ class RxRoom(private val room: Room) {
|
||||
room.invite3pid(threePid, it)
|
||||
}
|
||||
|
||||
fun updateTopic(topic: String): Completable = completableBuilder<Unit> {
|
||||
room.updateTopic(topic, it)
|
||||
fun updateTopic(topic: String): Completable = rxCompletable {
|
||||
room.updateTopic(topic)
|
||||
}
|
||||
|
||||
fun updateName(name: String): Completable = completableBuilder<Unit> {
|
||||
room.updateName(name, it)
|
||||
fun updateName(name: String): Completable = rxCompletable {
|
||||
room.updateName(name)
|
||||
}
|
||||
|
||||
fun addRoomAlias(alias: String): Completable = completableBuilder<Unit> {
|
||||
room.addRoomAlias(alias, it)
|
||||
fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = rxCompletable {
|
||||
room.updateHistoryReadability(readability)
|
||||
}
|
||||
|
||||
fun updateCanonicalAlias(alias: String): Completable = completableBuilder<Unit> {
|
||||
room.updateCanonicalAlias(alias, it)
|
||||
fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?): Completable = rxCompletable {
|
||||
room.updateJoinRule(joinRules, guestAccess)
|
||||
}
|
||||
|
||||
fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = completableBuilder<Unit> {
|
||||
room.updateHistoryReadability(readability, it)
|
||||
fun updateAvatar(avatarUri: Uri, fileName: String): Completable = rxCompletable {
|
||||
room.updateAvatar(avatarUri, fileName)
|
||||
}
|
||||
|
||||
fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> {
|
||||
room.updateAvatar(avatarUri, fileName, it)
|
||||
}
|
||||
|
||||
fun deleteAvatar(): Completable = completableBuilder<Unit> {
|
||||
room.deleteAvatar(it)
|
||||
fun deleteAvatar(): Completable = rxCompletable {
|
||||
room.deleteAvatar()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -47,6 +47,7 @@ import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||
|
||||
class RxSession(private val session: Session) {
|
||||
|
||||
@@ -139,7 +140,7 @@ class RxSession(private val session: Session) {
|
||||
}
|
||||
|
||||
fun getRoomIdByAlias(roomAlias: String,
|
||||
searchOnServer: Boolean): Single<Optional<String>> = singleBuilder {
|
||||
searchOnServer: Boolean): Single<Optional<RoomAliasDescription>> = singleBuilder {
|
||||
session.getRoomIdByAlias(roomAlias, searchOnServer, it)
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'realm-android'
|
||||
|
||||
buildscript {
|
||||
@@ -9,21 +9,17 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "io.realm:realm-gradle-plugin:10.0.0"
|
||||
classpath "io.realm:realm-gradle-plugin:10.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
testOptions.unitTests.includeAndroidResources = true
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "0.0.1"
|
||||
// Multidex is useful for tests
|
||||
@@ -63,7 +59,7 @@ android {
|
||||
|
||||
release {
|
||||
buildConfigField "boolean", "LOG_PRIVATE_DATA", "false"
|
||||
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.NONE"
|
||||
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.BASIC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +112,7 @@ dependencies {
|
||||
def lifecycle_version = '2.2.0'
|
||||
def arch_version = '2.1.0'
|
||||
def markwon_version = '3.1.0'
|
||||
def daggerVersion = '2.29.1'
|
||||
def daggerVersion = '2.31'
|
||||
def work_version = '2.4.0'
|
||||
def retrofit_version = '2.6.2'
|
||||
|
||||
@@ -125,7 +121,6 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||
implementation "androidx.fragment:fragment:1.3.0-beta01"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
@@ -146,7 +141,7 @@ dependencies {
|
||||
implementation "ru.noties.markwon:core:$markwon_version"
|
||||
|
||||
// Image
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.1'
|
||||
|
||||
// Database
|
||||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||
@@ -165,16 +160,11 @@ dependencies {
|
||||
// DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'
|
||||
|
||||
// Logging
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
|
||||
|
||||
// Bus
|
||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
|
||||
|
||||
|
8
matrix-sdk-android/proguard-rules.pro
vendored
8
matrix-sdk-android/proguard-rules.pro
vendored
@@ -20,14 +20,8 @@
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
|
||||
### EVENT BUS ###
|
||||
|
||||
# BMA: Not sure I can delete this one without side effect
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class * {
|
||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||
}
|
||||
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||
|
||||
### MOSHI ###
|
||||
|
||||
|
@@ -16,8 +16,18 @@
|
||||
|
||||
package org.matrix.android.sdk.account
|
||||
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
@@ -25,12 +35,8 @@ import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@@ -44,7 +50,18 @@ class DeactivateAccountTest : InstrumentedTest {
|
||||
|
||||
// Deactivate the account
|
||||
commonTestHelper.runBlockingTest {
|
||||
session.deactivateAccount(TestConstants.PASSWORD, false)
|
||||
session.deactivateAccount(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = session.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
|
||||
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
|
||||
|
@@ -25,6 +25,7 @@ import androidx.work.WorkManager
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
||||
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
|
||||
import org.matrix.android.sdk.api.raw.RawService
|
||||
import org.matrix.android.sdk.common.DaggerTestMatrixComponent
|
||||
@@ -49,6 +50,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||
@Inject internal lateinit var olmManager: OlmManager
|
||||
@Inject internal lateinit var sessionManager: SessionManager
|
||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||
|
||||
private val uiHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
@@ -71,6 +73,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||
|
||||
fun rawService() = rawService
|
||||
|
||||
fun homeServerHistoryService() = homeServerHistoryService
|
||||
|
||||
fun legacySessionImporter(): LegacySessionImporter {
|
||||
return legacySessionImporter
|
||||
}
|
||||
|
@@ -86,7 +86,7 @@ class CommonTestHelper(context: Context) {
|
||||
*
|
||||
* @param session the session to sync
|
||||
*/
|
||||
fun syncSession(session: Session) {
|
||||
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
|
||||
val lock = CountDownLatch(1)
|
||||
|
||||
val job = GlobalScope.launch(Dispatchers.Main) {
|
||||
@@ -109,7 +109,7 @@ class CommonTestHelper(context: Context) {
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
|
||||
|
||||
await(lock)
|
||||
await(lock, timeout)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +119,7 @@ class CommonTestHelper(context: Context) {
|
||||
* @param message the message to send
|
||||
* @param nbOfMessages the number of time the message will be sent
|
||||
*/
|
||||
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List<TimelineEvent> {
|
||||
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
||||
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
|
||||
val latch = CountDownLatch(1)
|
||||
@@ -151,7 +151,7 @@ class CommonTestHelper(context: Context) {
|
||||
room.sendTextMessage(message + " #" + (i + 1))
|
||||
}
|
||||
// Wait 3 second more per message
|
||||
await(latch, timeout = TestConstants.timeOutMillis + 3_000L * nbOfMessages)
|
||||
await(latch, timeout = timeout + 3_000L * nbOfMessages)
|
||||
timeline.dispose()
|
||||
|
||||
// Check that all events has been created
|
||||
@@ -215,14 +215,14 @@ class CommonTestHelper(context: Context) {
|
||||
.getLoginFlow(hs, it)
|
||||
}
|
||||
|
||||
doSync<RegistrationResult> {
|
||||
doSync<RegistrationResult>(timeout = 60_000) {
|
||||
matrix.authenticationService
|
||||
.getRegistrationWizard()
|
||||
.createAccount(userName, password, null, it)
|
||||
}
|
||||
|
||||
// Perform dummy step
|
||||
val registrationResult = doSync<RegistrationResult> {
|
||||
val registrationResult = doSync<RegistrationResult>(timeout = 60_000) {
|
||||
matrix.authenticationService
|
||||
.getRegistrationWizard()
|
||||
.dummy(it)
|
||||
@@ -231,7 +231,7 @@ class CommonTestHelper(context: Context) {
|
||||
assertTrue(registrationResult is RegistrationResult.Success)
|
||||
val session = (registrationResult as RegistrationResult.Success).session
|
||||
if (sessionTestParams.withInitialSync) {
|
||||
syncSession(session)
|
||||
syncSession(session, 60_000)
|
||||
}
|
||||
|
||||
return session
|
||||
@@ -378,7 +378,9 @@ class CommonTestHelper(context: Context) {
|
||||
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
|
||||
|
||||
fun signOutAndClose(session: Session) {
|
||||
doSync<Unit>(60_000) { session.signOut(true, it) }
|
||||
runBlockingTest(timeout = 60_000) {
|
||||
session.signOut(true)
|
||||
}
|
||||
// no need signout will close
|
||||
// session.close()
|
||||
}
|
||||
|
@@ -18,14 +18,21 @@ package org.matrix.android.sdk.common
|
||||
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
||||
data class CryptoTestData(val firstSession: Session,
|
||||
val roomId: String,
|
||||
val secondSession: Session? = null,
|
||||
val thirdSession: Session? = null) {
|
||||
data class CryptoTestData(val roomId: String,
|
||||
val sessions: List<Session>) {
|
||||
|
||||
val firstSession: Session
|
||||
get() = sessions.first()
|
||||
|
||||
val secondSession: Session?
|
||||
get() = sessions.getOrNull(1)
|
||||
|
||||
val thirdSession: Session?
|
||||
get() = sessions.getOrNull(2)
|
||||
|
||||
fun cleanUp(testHelper: CommonTestHelper) {
|
||||
testHelper.signOutAndClose(firstSession)
|
||||
secondSession?.let { testHelper.signOutAndClose(it) }
|
||||
thirdSession?.let { testHelper.signOutAndClose(it) }
|
||||
sessions.forEach {
|
||||
testHelper.signOutAndClose(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,18 @@ package org.matrix.android.sdk.common
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Observer
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
||||
@@ -36,17 +48,10 @@ import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
|
||||
@@ -73,7 +78,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
}
|
||||
}
|
||||
|
||||
return CryptoTestData(aliceSession, roomId)
|
||||
return CryptoTestData(roomId, listOf(aliceSession))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +144,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
// assertNotNull(roomFromBobPOV.powerLevels)
|
||||
// assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId))
|
||||
|
||||
return CryptoTestData(aliceSession, aliceRoomId, bobSession)
|
||||
return CryptoTestData(aliceRoomId, listOf(aliceSession, bobSession))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +162,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
// wait the initial sync
|
||||
SystemClock.sleep(1000)
|
||||
|
||||
return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession)
|
||||
return CryptoTestData(aliceRoomId, listOf(aliceSession, cryptoTestData.secondSession!!, samSession))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,10 +309,18 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
fun initializeCrossSigning(session: Session) {
|
||||
mTestHelper.doSync<Unit> {
|
||||
session.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = session.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = session.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,4 +394,30 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
|
||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
|
||||
|
||||
val roomId = mTestHelper.doSync<String> {
|
||||
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, it)
|
||||
}
|
||||
val room = aliceSession.getRoom(roomId)!!
|
||||
|
||||
mTestHelper.runBlockingTest {
|
||||
room.enableEncryption()
|
||||
}
|
||||
|
||||
val sessions = mutableListOf(aliceSession)
|
||||
for (index in 1 until numberOfMembers) {
|
||||
val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
|
||||
mTestHelper.doSync<Unit>(timeout = 600_000) { room.invite(session.myUserId, null, it) }
|
||||
println("TEST -> " + session.myUserId + " invited")
|
||||
mTestHelper.doSync<Unit> { session.joinRoom(room.roomId, null, emptyList(), it) }
|
||||
println("TEST -> " + session.myUserId + " joined")
|
||||
sessions.add(session)
|
||||
}
|
||||
|
||||
return CryptoTestData(roomId, sessions)
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,18 @@
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
@@ -30,19 +41,13 @@ import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.olm.OlmSession
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
/**
|
||||
* Ref:
|
||||
@@ -202,10 +207,18 @@ class UnwedgingTest : InstrumentedTest {
|
||||
// It's a trick to force key request on fail to decrypt
|
||||
mTestHelper.doSync<Unit> {
|
||||
bobSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
// Wait until we received back the key
|
||||
|
@@ -17,14 +17,6 @@
|
||||
package org.matrix.android.sdk.internal.crypto.crosssigning
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
@@ -35,6 +27,19 @@ import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@@ -49,10 +54,17 @@ class XSigningTest : InstrumentedTest {
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys()
|
||||
@@ -86,8 +98,18 @@ class XSigningTest : InstrumentedTest {
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
|
||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(aliceAuthParams)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(bobAuthParams)
|
||||
}
|
||||
}, it) }
|
||||
|
||||
// Check that alice can see bob keys
|
||||
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
|
||||
@@ -122,8 +144,16 @@ class XSigningTest : InstrumentedTest {
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
|
||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) }
|
||||
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(aliceAuthParams)
|
||||
}
|
||||
}, it) }
|
||||
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(bobAuthParams)
|
||||
}
|
||||
}, it) }
|
||||
|
||||
// Check that alice can see bob keys
|
||||
val bobUserId = bobSession.myUserId
|
||||
|
@@ -17,13 +17,13 @@
|
||||
package org.matrix.android.sdk.internal.crypto.encryption
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
@@ -57,13 +57,14 @@ class EncryptionTest : InstrumentedTest {
|
||||
@Test
|
||||
fun test_EncryptionStateEvent() {
|
||||
performTest(roomShouldBeEncrypted = true) { room ->
|
||||
// Send an encryption Event as a State Event
|
||||
room.sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
stateKey = null,
|
||||
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent(),
|
||||
callback = NoOpMatrixCallback()
|
||||
)
|
||||
runBlocking {
|
||||
// Send an encryption Event as a State Event
|
||||
room.sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
stateKey = null,
|
||||
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,21 @@ package org.matrix.android.sdk.internal.crypto.gossiping
|
||||
|
||||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
@@ -28,6 +42,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
@@ -40,17 +55,9 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@@ -198,10 +205,17 @@ class KeyShareTests : InstrumentedTest {
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession1.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = aliceSession1.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), it)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession1.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
// Also bootstrap keybackup on first session
|
||||
@@ -296,4 +310,93 @@ class KeyShareTests : InstrumentedTest {
|
||||
mTestHelper.signOutAndClose(aliceSession1)
|
||||
mTestHelper.signOutAndClose(aliceSession2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_ImproperKeyShareBug() {
|
||||
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
// Create an encrypted room and send a couple of messages
|
||||
val roomId = mTestHelper.doSync<String> {
|
||||
aliceSession.createRoom(
|
||||
CreateRoomParams().apply {
|
||||
visibility = RoomDirectoryVisibility.PRIVATE
|
||||
enableEncryption()
|
||||
},
|
||||
it
|
||||
)
|
||||
}
|
||||
val roomAlicePov = aliceSession.getRoom(roomId)
|
||||
assertNotNull(roomAlicePov)
|
||||
Thread.sleep(1_000)
|
||||
assertTrue(roomAlicePov?.isEncrypted() == true)
|
||||
val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
|
||||
|
||||
// Create bob session
|
||||
|
||||
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
||||
mTestHelper.doSync<Unit> {
|
||||
bobSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
// Let alice invite bob
|
||||
mTestHelper.doSync<Unit> {
|
||||
roomAlicePov.invite(bobSession.myUserId, null, it)
|
||||
}
|
||||
|
||||
mTestHelper.doSync<Unit> {
|
||||
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList(), it)
|
||||
}
|
||||
|
||||
// we want to discard alice outbound session
|
||||
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
|
||||
|
||||
// and now resend a new message to reset index to 0
|
||||
mTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
||||
|
||||
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
||||
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
|
||||
|
||||
var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
|
||||
|
||||
assert(dRes == null)
|
||||
|
||||
// Try to re-ask the keys
|
||||
|
||||
bobSession.cryptoService().reRequestRoomKeyForEvent(beforeJoin!!.root)
|
||||
|
||||
Thread.sleep(3_000)
|
||||
|
||||
// With the bug the first session would have improperly reshare that key :/
|
||||
dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin.root, "") }
|
||||
Log.d("#TEST", "KS: sgould not decrypt that ${beforeJoin.root.getClearContent().toModel<MessageContent>()?.body}")
|
||||
assert(dRes?.clearEvent == null)
|
||||
}
|
||||
}
|
||||
|
@@ -264,7 +264,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertNotNull(decryption)
|
||||
// - Check decryptKeyBackupData() returns stg
|
||||
val sessionData = keysBackup
|
||||
.decryptKeyBackupData(keyBackupData!!,
|
||||
.decryptKeyBackupData(keyBackupData,
|
||||
session.olmInboundGroupSession!!.sessionIdentifier(),
|
||||
cryptoTestData.roomId,
|
||||
decryption!!)
|
||||
|
@@ -111,7 +111,7 @@ class KeysBackupTestHelper(
|
||||
Assert.assertTrue(keysBackup.isEnabled)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
return PrepareKeysBackupDataResult(megolmBackupCreationInfo, keysVersion.version!!)
|
||||
return PrepareKeysBackupDataResult(megolmBackupCreationInfo, keysVersion.version)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -17,20 +17,25 @@
|
||||
package org.matrix.android.sdk.internal.crypto.verification.qrcode
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@@ -157,18 +162,34 @@ class VerificationTest : InstrumentedTest {
|
||||
|
||||
mTestHelper.doSync<Unit> { callback ->
|
||||
aliceSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), callback)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, callback)
|
||||
}
|
||||
|
||||
mTestHelper.doSync<Unit> { callback ->
|
||||
bobSession.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
), callback)
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = bobSession.myUserId,
|
||||
password = TestConstants.PASSWORD,
|
||||
session = flowResponse.session
|
||||
)
|
||||
)
|
||||
}
|
||||
}, callback)
|
||||
}
|
||||
|
||||
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
||||
|
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.session.media
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
internal class UrlsExtractorTest : InstrumentedTest {
|
||||
|
||||
private val urlsExtractor = UrlsExtractor()
|
||||
|
||||
@Test
|
||||
fun wrongEventTypeTest() {
|
||||
createEvent(body = "https://matrix.org")
|
||||
.copy(type = EventType.STATE_ROOM_GUEST_ACCESS)
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.size shouldBeEqualTo 0
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oneUrlTest() {
|
||||
createEvent(body = "https://matrix.org")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
result[0] shouldBeEqualTo "https://matrix.org"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun withoutProtocolTest() {
|
||||
createEvent(body = "www.matrix.org")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.size shouldBeEqualTo 0
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oneUrlWithParamTest() {
|
||||
createEvent(body = "https://matrix.org?foo=bar")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
result[0] shouldBeEqualTo "https://matrix.org?foo=bar"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oneUrlWithParamsTest() {
|
||||
createEvent(body = "https://matrix.org?foo=bar&bar=foo")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
result[0] shouldBeEqualTo "https://matrix.org?foo=bar&bar=foo"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oneUrlInlinedTest() {
|
||||
createEvent(body = "Hello https://matrix.org, how are you?")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 1
|
||||
result[0] shouldBeEqualTo "https://matrix.org"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun twoUrlsTest() {
|
||||
createEvent(body = "https://matrix.org https://example.org")
|
||||
.toFakeTimelineEvent()
|
||||
.let { urlsExtractor.extract(it) }
|
||||
.let { result ->
|
||||
result.size shouldBeEqualTo 2
|
||||
result[0] shouldBeEqualTo "https://matrix.org"
|
||||
result[1] shouldBeEqualTo "https://example.org"
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEvent(body: String): Event = Event(
|
||||
eventId = "!fake",
|
||||
type = EventType.MESSAGE,
|
||||
content = MessageTextContent(
|
||||
msgType = MessageType.MSGTYPE_TEXT,
|
||||
body = body
|
||||
).toContent()
|
||||
)
|
||||
|
||||
private fun Event.toFakeTimelineEvent(): TimelineEvent {
|
||||
return TimelineEvent(
|
||||
root = this,
|
||||
localId = 0L,
|
||||
eventId = eventId!!,
|
||||
displayIndex = 0,
|
||||
senderInfo = SenderInfo(
|
||||
userId = "",
|
||||
displayName = null,
|
||||
isUniqueDisplayName = true,
|
||||
avatarUrl = null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -66,8 +66,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
||||
numberOfMessagesToSend)
|
||||
|
||||
// Alice clear the cache
|
||||
commonTestHelper.doSync<Unit> {
|
||||
aliceSession.clearCache(it)
|
||||
commonTestHelper.runBlockingTest {
|
||||
aliceSession.clearCache()
|
||||
}
|
||||
|
||||
// And restarts the sync
|
||||
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.session.room.timeline
|
||||
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.test.fail
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class TimelineWithManyMembersTest : InstrumentedTest {
|
||||
|
||||
companion object {
|
||||
private const val NUMBER_OF_MEMBERS = 6
|
||||
}
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
/**
|
||||
* Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
|
||||
*/
|
||||
@Test
|
||||
fun everyone_should_decrypt_message_in_a_crowded_room() {
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)
|
||||
|
||||
val sessionForFirstMember = cryptoTestData.firstSession
|
||||
val roomForFirstMember = sessionForFirstMember.getRoom(cryptoTestData.roomId)!!
|
||||
|
||||
val firstMessage = "First messages from Alice"
|
||||
commonTestHelper.sendTextMessage(
|
||||
roomForFirstMember,
|
||||
firstMessage,
|
||||
1,
|
||||
600_000
|
||||
)
|
||||
|
||||
for (index in 1 until cryptoTestData.sessions.size) {
|
||||
val session = cryptoTestData.sessions[index]
|
||||
val roomForCurrentMember = session.getRoom(cryptoTestData.roomId)!!
|
||||
val timelineForCurrentMember = roomForCurrentMember.createTimeline(null, TimelineSettings(30))
|
||||
timelineForCurrentMember.start()
|
||||
|
||||
session.startSync(true)
|
||||
|
||||
run {
|
||||
val lock = CountDownLatch(1)
|
||||
val eventsListener = commonTestHelper.createEventListener(lock) { snapshot ->
|
||||
snapshot
|
||||
.find { it.isEncrypted() }
|
||||
?.let {
|
||||
val body = it.root.getClearContent()?.toModel<MessageContent>()?.body
|
||||
if (body?.startsWith(firstMessage).orFalse()) {
|
||||
println("User " + session.myUserId + " decrypted as " + body)
|
||||
return@createEventListener true
|
||||
} else {
|
||||
fail("User " + session.myUserId + " decrypted as " + body + " CryptoError: " + it.root.mCryptoError)
|
||||
}
|
||||
} ?: return@createEventListener false
|
||||
}
|
||||
timelineForCurrentMember.addListener(eventsListener)
|
||||
commonTestHelper.await(lock, 600_000)
|
||||
}
|
||||
session.stopSync()
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,7 +17,6 @@
|
||||
package org.matrix.android.sdk.internal.network.interceptors
|
||||
|
||||
import androidx.annotation.NonNull
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
@@ -38,31 +37,28 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
|
||||
*/
|
||||
@Synchronized
|
||||
override fun log(@NonNull message: String) {
|
||||
// In RELEASE there is no log, but for sure, test again BuildConfig.DEBUG
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.v(message)
|
||||
Timber.v(message)
|
||||
|
||||
if (message.startsWith("{")) {
|
||||
// JSON Detected
|
||||
try {
|
||||
val o = JSONObject(message)
|
||||
logJson(o.toString(INDENT_SPACE))
|
||||
} catch (e: JSONException) {
|
||||
// Finally this is not a JSON string...
|
||||
Timber.e(e)
|
||||
}
|
||||
} else if (message.startsWith("[")) {
|
||||
// JSON Array detected
|
||||
try {
|
||||
val o = JSONArray(message)
|
||||
logJson(o.toString(INDENT_SPACE))
|
||||
} catch (e: JSONException) {
|
||||
// Finally not JSON...
|
||||
Timber.e(e)
|
||||
}
|
||||
if (message.startsWith("{")) {
|
||||
// JSON Detected
|
||||
try {
|
||||
val o = JSONObject(message)
|
||||
logJson(o.toString(INDENT_SPACE))
|
||||
} catch (e: JSONException) {
|
||||
// Finally this is not a JSON string...
|
||||
Timber.e(e)
|
||||
}
|
||||
} else if (message.startsWith("[")) {
|
||||
// JSON Array detected
|
||||
try {
|
||||
val o = JSONArray(message)
|
||||
logJson(o.toString(INDENT_SPACE))
|
||||
} catch (e: JSONException) {
|
||||
// Finally not JSON...
|
||||
Timber.e(e)
|
||||
}
|
||||
// Else not a json string to log
|
||||
}
|
||||
// Else not a json string to log
|
||||
}
|
||||
|
||||
private fun logJson(formattedJson: String) {
|
||||
|
@@ -23,6 +23,7 @@ import androidx.work.WorkManager
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
||||
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
|
||||
import org.matrix.android.sdk.api.raw.RawService
|
||||
import org.matrix.android.sdk.internal.SessionManager
|
||||
@@ -47,6 +48,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||
@Inject internal lateinit var olmManager: OlmManager
|
||||
@Inject internal lateinit var sessionManager: SessionManager
|
||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||
|
||||
init {
|
||||
Monarchy.init(context)
|
||||
@@ -65,6 +67,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||
|
||||
fun rawService() = rawService
|
||||
|
||||
fun homeServerHistoryService() = homeServerHistoryService
|
||||
|
||||
fun legacySessionImporter(): LegacySessionImporter {
|
||||
return legacySessionImporter
|
||||
}
|
||||
|
@@ -41,6 +41,16 @@ interface AuthenticationService {
|
||||
*/
|
||||
fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
|
||||
|
||||
/**
|
||||
* Get a SSO url
|
||||
*/
|
||||
fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String?
|
||||
|
||||
/**
|
||||
* Get the sign in or sign up fallback URL
|
||||
*/
|
||||
fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String?
|
||||
|
||||
/**
|
||||
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
|
||||
*/
|
||||
|
@@ -14,18 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.eventbus
|
||||
package org.matrix.android.sdk.api.auth
|
||||
|
||||
import org.greenrobot.eventbus.Logger
|
||||
import timber.log.Timber
|
||||
import java.util.logging.Level
|
||||
/**
|
||||
* A simple service to remember homeservers you already connected to.
|
||||
*/
|
||||
interface HomeServerHistoryService {
|
||||
|
||||
class EventBusTimberLogger : Logger {
|
||||
override fun log(level: Level, msg: String) {
|
||||
Timber.d(msg)
|
||||
}
|
||||
fun getKnownServersUrls(): List<String>
|
||||
|
||||
override fun log(level: Level, msg: String, th: Throwable) {
|
||||
Timber.e(th, msg)
|
||||
}
|
||||
fun addHomeServerToHistory(url: String)
|
||||
|
||||
fun clearHistory()
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.auth
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
|
||||
/**
|
||||
* This class provides the authentication data by using user and password
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TokenBasedAuth(
|
||||
|
||||
/**
|
||||
* This is a session identifier that the client must pass back to the homeserver,
|
||||
* if one is provided, in subsequent attempts to authenticate in the same API call.
|
||||
*/
|
||||
@Json(name = "session")
|
||||
override val session: String? = null,
|
||||
|
||||
/**
|
||||
* A client may receive a login token via some external service, such as email or SMS.
|
||||
* Note that a login token is separate from an access token, the latter providing general authentication to various API endpoints.
|
||||
*/
|
||||
@Json(name = "token")
|
||||
val token: String? = null,
|
||||
|
||||
/**
|
||||
* The txn_id should be a random string generated by the client for the request.
|
||||
* The same txn_id should be used if retrying the request.
|
||||
* The txn_id may be used by the server to disallow other devices from using the token,
|
||||
* thus providing "single use" tokens while still allowing the device to retry the request.
|
||||
* This would be done by tying the token to the txn_id server side, as well as potentially invalidating
|
||||
* the token completely once the device has successfully logged in
|
||||
* (e.g. when we receive a request from the newly provisioned access_token).
|
||||
*/
|
||||
@Json(name = "txn_id")
|
||||
val transactionId: String? = null,
|
||||
|
||||
// registration information
|
||||
@Json(name = "type")
|
||||
val type: String? = LoginFlowTypes.TOKEN
|
||||
|
||||
) : UIABaseAuth {
|
||||
override fun hasAuthInfo() = token != null
|
||||
|
||||
override fun copyWithSession(session: String) = this.copy(session = session)
|
||||
|
||||
override fun asMap(): Map<String, *> = mapOf(
|
||||
"session" to session,
|
||||
"token" to token,
|
||||
"transactionId" to transactionId,
|
||||
"type" to type
|
||||
)
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.auth
|
||||
|
||||
interface UIABaseAuth {
|
||||
/**
|
||||
* This is a session identifier that the client must pass back to the homeserver,
|
||||
* if one is provided, in subsequent attempts to authenticate in the same API call.
|
||||
*/
|
||||
val session: String?
|
||||
|
||||
fun hasAuthInfo(): Boolean
|
||||
|
||||
fun copyWithSession(session: String): UIABaseAuth
|
||||
|
||||
fun asMap() : Map<String, *>
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.auth
|
||||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import kotlin.coroutines.Continuation
|
||||
|
||||
/**
|
||||
* Some API endpoints require authentication that interacts with the user.
|
||||
* The homeserver may provide many different ways of authenticating, such as user/password auth, login via a social network (OAuth2),
|
||||
* login by confirming a token sent to their email address, etc.
|
||||
*
|
||||
* The process takes the form of one or more 'stages'.
|
||||
* At each stage the client submits a set of data for a given authentication type and awaits a response from the server,
|
||||
* which will either be a final success or a request to perform an additional stage.
|
||||
* This exchange continues until the final success.
|
||||
*
|
||||
* For each endpoint, a server offers one or more 'flows' that the client can use to authenticate itself.
|
||||
* Each flow comprises a series of stages, as described above.
|
||||
* The client is free to choose which flow it follows, however the flow's stages must be completed in order.
|
||||
* Failing to follow the flows in order must result in an HTTP 401 response.
|
||||
* When all stages in a flow are complete, authentication is complete and the API call succeeds.
|
||||
*/
|
||||
interface UserInteractiveAuthInterceptor {
|
||||
|
||||
/**
|
||||
* When the API needs additional auth, this will be called.
|
||||
* Implementation should check the flows from flow response and act accordingly.
|
||||
* Updated auth should be provided using promise.resume, this allow implementation to perform
|
||||
* an async operation (prompt for user password, open sso fallback) and then resume initial API call when done.
|
||||
*/
|
||||
fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>)
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.matrix.android.sdk.internal.crypto.model.rest
|
||||
package org.matrix.android.sdk.api.auth
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
@@ -27,7 +27,7 @@ data class UserPasswordAuth(
|
||||
|
||||
// device device session id
|
||||
@Json(name = "session")
|
||||
val session: String? = null,
|
||||
override val session: String? = null,
|
||||
|
||||
// registration information
|
||||
@Json(name = "type")
|
||||
@@ -38,4 +38,16 @@ data class UserPasswordAuth(
|
||||
|
||||
@Json(name = "password")
|
||||
val password: String? = null
|
||||
)
|
||||
) : UIABaseAuth {
|
||||
|
||||
override fun hasAuthInfo() = password != null
|
||||
|
||||
override fun copyWithSession(session: String) = this.copy(session = session)
|
||||
|
||||
override fun asMap(): Map<String, *> = mapOf(
|
||||
"session" to session,
|
||||
"user" to user,
|
||||
"password" to password,
|
||||
"type" to type
|
||||
)
|
||||
}
|
@@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.auth.data
|
||||
sealed class LoginFlowResult {
|
||||
data class Success(
|
||||
val supportedLoginTypes: List<String>,
|
||||
val ssoIdentityProviders: List<SsoIdentityProvider>?,
|
||||
val isLoginAndRegistrationSupported: Boolean,
|
||||
val homeServerUrl: String,
|
||||
val isOutdatedHomeserver: Boolean
|
||||
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.auth.data
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class SsoIdentityProvider(
|
||||
/**
|
||||
* The id field would be opaque with the accepted characters matching unreserved URI characters as defined in RFC3986
|
||||
* - this was chosen to avoid having to encode special characters in the URL. Max length 128.
|
||||
*/
|
||||
@Json(name = "id") val id: String,
|
||||
/**
|
||||
* The name field should be the human readable string intended for printing by the client.
|
||||
*/
|
||||
@Json(name = "name") val name: String?,
|
||||
/**
|
||||
* The icon field is the only optional field and should point to an icon representing the IdP.
|
||||
* If present then it must be an HTTPS URL to an image resource.
|
||||
* This should be hosted by the homeserver service provider to not leak the client's IP address unnecessarily.
|
||||
*/
|
||||
@Json(name = "icon") val iconUrl: String?,
|
||||
|
||||
/**
|
||||
* The `brand` field is **optional**. It allows the client to style the login
|
||||
* button to suit a particular brand. It should be a string matching the
|
||||
* "Common namespaced identifier grammar" as defined in
|
||||
* [MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758).
|
||||
*/
|
||||
@Json(name = "brand") val brand: String?
|
||||
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
const val BRAND_GOOGLE = "org.matrix.google"
|
||||
const val BRAND_GITHUB = "org.matrix.github"
|
||||
const val BRAND_APPLE = "org.matrix.apple"
|
||||
const val BRAND_FACEBOOK = "org.matrix.facebook"
|
||||
const val BRAND_TWITTER = "org.matrix.twitter"
|
||||
const val BRAND_GITLAB = "org.matrix.gitlab"
|
||||
}
|
||||
}
|
@@ -14,14 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.auth.registration
|
||||
package org.matrix.android.sdk.api.auth.registration
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||
import org.matrix.android.sdk.api.auth.registration.TermPolicies
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.auth.data.InteractiveAuthenticationFlow
|
||||
|
||||
@@ -51,6 +48,18 @@ data class RegistrationFlowResponse(
|
||||
* The information that the client will need to know in order to use a given type of authentication.
|
||||
* For each login stage type presented, that type may be present as a key in this dictionary.
|
||||
* For example, the public key of reCAPTCHA stage could be given here.
|
||||
* other example
|
||||
* "params": {
|
||||
* "m.login.sso": {
|
||||
* "identity_providers": [
|
||||
* {
|
||||
* "id": "google",
|
||||
* "name": "Google",
|
||||
* "icon": "https://..."
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@Json(name = "params")
|
||||
val params: JsonDict? = null
|
||||
@@ -97,3 +106,8 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
|
||||
|
||||
return FlowResult(missingStage, completedStage)
|
||||
}
|
||||
|
||||
fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? {
|
||||
val completed = completedStages ?: emptyList()
|
||||
return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() }
|
||||
}
|
@@ -14,16 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.raw
|
||||
package org.matrix.android.sdk.api.cache
|
||||
|
||||
sealed class RawCacheStrategy {
|
||||
sealed class CacheStrategy {
|
||||
// Data is always fetched from the server
|
||||
object NoCache : RawCacheStrategy()
|
||||
object NoCache : CacheStrategy()
|
||||
|
||||
// Once data is retrieved, it is stored for the provided amount of time.
|
||||
// In case of error, and if strict is set to false, the cache can be returned if available
|
||||
data class TtlCache(val validityDurationInMillis: Long, val strict: Boolean) : RawCacheStrategy()
|
||||
data class TtlCache(val validityDurationInMillis: Long, val strict: Boolean) : CacheStrategy()
|
||||
|
||||
// Once retrieved, the data is stored in cache and will be always get from the cache
|
||||
object InfiniteCache : RawCacheStrategy()
|
||||
object InfiniteCache : CacheStrategy()
|
||||
}
|
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.failure
|
||||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import java.io.IOException
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
@@ -43,6 +43,12 @@ fun Throwable.isInvalidPassword(): Boolean {
|
||||
&& error.message == "Invalid password"
|
||||
}
|
||||
|
||||
fun Throwable.isInvalidUIAAuth(): Boolean {
|
||||
return this is Failure.ServerError
|
||||
&& error.code == MatrixError.M_FORBIDDEN
|
||||
&& error.flows != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
||||
*/
|
||||
@@ -53,6 +59,16 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
|
||||
.adapter(RegistrationFlowResponse::class.java)
|
||||
.fromJson(this.errorBody)
|
||||
}
|
||||
} else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) {
|
||||
// This happens when the submission for this stage was bad (like bad password)
|
||||
if (this.error.session != null && this.error.flows != null) {
|
||||
RegistrationFlowResponse(
|
||||
flows = this.error.flows,
|
||||
session = this.error.session,
|
||||
completedStages = this.error.completedStages,
|
||||
params = this.error.params
|
||||
)
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.failure
|
||||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
|
||||
import java.io.IOException
|
||||
|
||||
|
@@ -18,6 +18,8 @@ package org.matrix.android.sdk.api.failure
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.auth.data.InteractiveAuthenticationFlow
|
||||
|
||||
/**
|
||||
* This data class holds the error defined by the matrix specifications.
|
||||
@@ -42,7 +44,17 @@ data class MatrixError(
|
||||
@Json(name = "soft_logout") val isSoftLogout: Boolean = false,
|
||||
// For M_INVALID_PEPPER
|
||||
// {"error": "pepper does not match 'erZvr'", "lookup_pepper": "pQgMS", "algorithm": "sha256", "errcode": "M_INVALID_PEPPER"}
|
||||
@Json(name = "lookup_pepper") val newLookupPepper: String? = null
|
||||
@Json(name = "lookup_pepper") val newLookupPepper: String? = null,
|
||||
|
||||
// For M_FORBIDDEN UIA
|
||||
@Json(name = "session")
|
||||
val session: String? = null,
|
||||
@Json(name = "completed")
|
||||
val completedStages: List<String>? = null,
|
||||
@Json(name = "flows")
|
||||
val flows: List<InteractiveAuthenticationFlow>? = null,
|
||||
@Json(name = "params")
|
||||
val params: JsonDict? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
@@ -37,6 +37,6 @@ class SenderNotificationPermissionCondition(
|
||||
|
||||
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
|
||||
val powerLevelsHelper = PowerLevelsHelper(powerLevels)
|
||||
return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevelsHelper.notificationLevel(key)
|
||||
return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevels.notificationLevel(key)
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.raw
|
||||
|
||||
import org.matrix.android.sdk.api.cache.CacheStrategy
|
||||
|
||||
/**
|
||||
* Useful methods to fetch raw data from the server. The access token will not be used to fetched the data
|
||||
*/
|
||||
@@ -23,7 +25,7 @@ interface RawService {
|
||||
/**
|
||||
* Get a URL, either from cache or from the remote server, depending on the cache strategy
|
||||
*/
|
||||
suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String
|
||||
suspend fun getUrl(url: String, cacheStrategy: CacheStrategy): String
|
||||
|
||||
/**
|
||||
* Specific case for the well-known file. Cache validity is 8 hours
|
||||
|
@@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.group.GroupService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||
@@ -181,6 +182,11 @@ interface Session :
|
||||
*/
|
||||
fun widgetService(): WidgetService
|
||||
|
||||
/**
|
||||
* Returns the media service associated with the session
|
||||
*/
|
||||
fun mediaService(): MediaService
|
||||
|
||||
/**
|
||||
* Returns the integration manager service associated with the session
|
||||
*/
|
||||
@@ -239,6 +245,8 @@ interface Session :
|
||||
|
||||
val sharedSecretStorageService: SharedSecretStorageService
|
||||
|
||||
fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
|
||||
|
||||
/**
|
||||
* Maintenance API, allows to print outs info on DB size to logcat
|
||||
*/
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user