mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-23 22:39:02 +03:00
Merge remote-tracking branch 'upstream/master' into sc
Change-Id: Icb4d672fc9d301803b300d6ac25b348780f11e6c Conflicts: gradle/wrapper/gradle-wrapper.properties vector/src/main/java/im/vector/app/features/home/HomeActivity.kt vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt vector/src/main/res/layout/fragment_known_users.xml vector/src/main/res/layout/fragment_user_list.xml
This commit is contained in:
commit
0b4f56d7a0
363 changed files with 7838 additions and 4011 deletions
AUTHORS.mdCHANGES.md
fastlane/metadata/android
ca
de/changelogs
en-US/changelogs
es
fa/changelogs
it/changelogs
nb
pt_BR/changelogs
sv/changelogs
zh_Hant
gradle/wrapper
matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx
matrix-sdk-android
build.gradle
src
androidTest/java/org/matrix/android/sdk
account
common
internal/crypto/verification
session/search
debug/java/org/matrix/android/sdk/internal/network/interceptors
main/java/org/matrix/android/sdk
api
auth/login
extensions
pushrules
raw
session
account
crypto/verification
events/model
group
identity
permalinks
room
search
user
internal
crypto
CryptoModule.ktDefaultCryptoService.ktEventDecryptor.ktIncomingGossipingRequestManager.ktMXOlmDevice.ktSendGossipWorker.kt
actions
algorithms/megolm
crosssigning
store/db
verification
database
network
raw
session
account
group
identity
notification
profile
room
DefaultRoom.ktDefaultRoomService.ktRoomAvatarResolver.kt
alias
create
draft
membership
notification
reporting
state
15
AUTHORS.md
15
AUTHORS.md
|
@ -4,7 +4,7 @@ A full developer contributors list can be found [here](https://github.com/vector
|
||||||
|
|
||||||
Even if we try to be able to work on all the functionalities, we have more knowledge about what we have developed ourselves.
|
Even if we try to be able to work on all the functionalities, we have more knowledge about what we have developed ourselves.
|
||||||
|
|
||||||
## Benoit: Android team leader
|
## [Benoit](https://github.com/bmarty): Android team leader
|
||||||
|
|
||||||
[@benoit.marty:matrix.org](https://matrix.to/#/@benoit.marty:matrix.org)
|
[@benoit.marty:matrix.org](https://matrix.to/#/@benoit.marty:matrix.org)
|
||||||
- Android team leader and project leader, Android developer, GitHub community manager.
|
- Android team leader and project leader, Android developer, GitHub community manager.
|
||||||
|
@ -12,7 +12,7 @@ Even if we try to be able to work on all the functionalities, we have more knowl
|
||||||
- Reviewing and polishing developed features, code quality manager, PRs reviewer, GitHub community manager.
|
- Reviewing and polishing developed features, code quality manager, PRs reviewer, GitHub community manager.
|
||||||
- Release manager on the Play Store
|
- Release manager on the Play Store
|
||||||
|
|
||||||
## François: Software architect
|
## [Ganfra](https://github.com/ganfra) (aka François): Software architect
|
||||||
|
|
||||||
[@ganfra:matrix.org](https://matrix.to/#/@ganfra:matrix.org)
|
[@ganfra:matrix.org](https://matrix.to/#/@ganfra:matrix.org)
|
||||||
- Software architect, Android developer
|
- Software architect, Android developer
|
||||||
|
@ -20,12 +20,17 @@ Even if we try to be able to work on all the functionalities, we have more knowl
|
||||||
- Work mainly on the global architecture of the project.
|
- Work mainly on the global architecture of the project.
|
||||||
- Specialist of the timeline, and lots of other features.
|
- Specialist of the timeline, and lots of other features.
|
||||||
|
|
||||||
## Valere: Product manager, Android developer
|
## [Valere](https://github.com/BillCarsonFr): Product manager, Android developer
|
||||||
|
|
||||||
[@valere35:matrix.org](https://matrix.to/#/@valere35:matrix.org)
|
[@valere35:matrix.org](https://matrix.to/#/@valere35:matrix.org)
|
||||||
- Product manager, Android developer
|
- Product manager, Android developer
|
||||||
- Specialist on the crypto implementation.
|
- Specialist on the crypto implementation.
|
||||||
|
|
||||||
|
## [Onuray](https://github.com/onurays): Android developer
|
||||||
|
|
||||||
|
[@onurays:matrix.org](https://matrix.to/#/@onurays:matrix.org)
|
||||||
|
- Android developer
|
||||||
|
|
||||||
# Other contributors
|
# Other contributors
|
||||||
|
|
||||||
First of all, we thank all contributors who use Element and report problems on this GitHub project or via the integrated rageshake function.
|
First of all, we thank all contributors who use Element and report problems on this GitHub project or via the integrated rageshake function.
|
||||||
|
@ -35,6 +40,6 @@ We do not forget all translators, for their work of translating Element into man
|
||||||
Feel free to add your name below, when you contribute to the project!
|
Feel free to add your name below, when you contribute to the project!
|
||||||
|
|
||||||
Name | Matrix ID | GitHub
|
Name | Matrix ID | GitHub
|
||||||
--------|---------------------|--------------------------------------
|
----------|-----------------------------|--------------------------------------
|
||||||
gjpower | @gjpower:matrix.org | [gjpower](https://github.com/gjpower)
|
gjpower | @gjpower:matrix.org | [gjpower](https://github.com/gjpower)
|
||||||
|
TR_SLimey | @tr_slimey:an-atom-in.space | [TR-SLimey](https://github.com/TR-SLimey)
|
||||||
|
|
45
CHANGES.md
45
CHANGES.md
|
@ -1,3 +1,45 @@
|
||||||
|
Changes in Element 1.0.11 (2020-11-27)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features ✨:
|
||||||
|
- Create DMs with users by scanning their QR code (#2025)
|
||||||
|
- Add Invite friends quick invite actions (#2348)
|
||||||
|
- Add friend by scanning QR code, show your code to friends (#2025)
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- New room creation tile with quick action (#2346)
|
||||||
|
- Open an existing DM instead of creating a new one (#2319)
|
||||||
|
- Use RoomMember instead of User in the context of a Room.
|
||||||
|
- Ask for explicit user consent to send their contact details to the identity server (#2375)
|
||||||
|
- Handle events of type "m.room.server_acl" (#890)
|
||||||
|
- Room creation form: add advanced section to disable federation (#1314)
|
||||||
|
- Move "Enable Encryption" from room setting screen to room profile screen (#2394)
|
||||||
|
- Home empty screens quick design update (#2347)
|
||||||
|
- Improve Invite user screen (seamless search for matrix ID)
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix crash on AttachmentViewer (#2365)
|
||||||
|
- Exclude yourself when decorating rooms which are direct or don't have more than 2 users (#2370)
|
||||||
|
- F-Droid version: ensure timeout of sync request can be more than 60 seconds (#2169)
|
||||||
|
- Fix issue when restoring draft after sharing (#2287)
|
||||||
|
- Fix issue when updating the avatar of a room (new avatar vanishing)
|
||||||
|
- Discard change dialog displayed by mistake when avatar has been updated
|
||||||
|
- Try to fix cropped image in timeline (#2126)
|
||||||
|
- Registration: annoying error message scares every new user when they add an email (#2391)
|
||||||
|
- Fix jitsi integration for those with non-vanilla dialler frameworks
|
||||||
|
- Update profile has no effect if user is in zero rooms
|
||||||
|
- Fix issues with matrix.to deep linking (#2349)
|
||||||
|
|
||||||
|
SDK API changes ⚠️:
|
||||||
|
- AccountService now exposes suspendable function instead of using MatrixCallback (#2354).
|
||||||
|
Note: We will incrementally migrate all the SDK API in a near future (#2449)
|
||||||
|
|
||||||
|
Test:
|
||||||
|
- Add `allScreensTest` to cover all screens of the app
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Upgrade Realm dependency to 10.0.0
|
||||||
|
|
||||||
Changes in Element 1.0.10 (2020-11-04)
|
Changes in Element 1.0.10 (2020-11-04)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
|
@ -1008,5 +1050,8 @@ SDK API changes ⚠️:
|
||||||
Build 🧱:
|
Build 🧱:
|
||||||
-
|
-
|
||||||
|
|
||||||
|
Test:
|
||||||
|
-
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
-
|
||||||
|
|
1
fastlane/metadata/android/ca/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/ca/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// TODO
|
30
fastlane/metadata/android/ca/full_description.txt
Normal file
30
fastlane/metadata/android/ca/full_description.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Element és un nou tipus d'aplicació de missatgeria i col·laboració que:
|
||||||
|
|
||||||
|
1. Et dóna a tu el control per preservar la teva privadesa
|
||||||
|
2. Et permet comunicar-te amb qualsevol persona de la xarxa Matrix i, fins i tot més enllà gràcies a integracions amb altres aplicacions com Slack
|
||||||
|
3. Et protegeix de la publicitat, l'obtenció no desitjada de dades i dels navegadors amb accés controlat
|
||||||
|
4. T'assegura a tu mitjançant l'encriptació d'extrem a extrem i amb signatures creuades per verificar els altres
|
||||||
|
|
||||||
|
Element és completament diferent a les altres aplicacions de missatgeria i col·laboració ja que és descentralitzat i de codi obert.
|
||||||
|
|
||||||
|
Element et deixa triar l'allotjament perquè disposis de privadesa, propietat i control de les teves dades i converses. Et dóna accés a una xarxa oberta perquè no et quedis únicament parlant amb els usuaris d'Element.
|
||||||
|
|
||||||
|
Element pot fer tot això ja que opera sobre Matrix - l'estàndard per a les comunicacions obertes i descentralitzades.
|
||||||
|
|
||||||
|
Element et dóna el control perquè et deixa escollir qui vols que allotgi les teves converses. Des de l'aplicació d'Element, pots triar l'allotjament de diferents maneres:
|
||||||
|
|
||||||
|
1. Crea un compte gratuït al servidor públic de matrix.org allotjat pels desenvolupadors de Matrix o tria'n un entre els milers de servidors públics creats per voluntaris
|
||||||
|
2. Allotja tu mateix el teu compte en el teu propi servidor
|
||||||
|
3. Registra el compte en un servidor personalitzat subscrivint-te a la plataforma d'Element Matrix Services (EMS)
|
||||||
|
|
||||||
|
<b>Per què escollir Element?</b>
|
||||||
|
|
||||||
|
<b>PROPIETAT DE LES TEVES DADES</b>: Tu decideixes a on desar les teves dades i missatges. Tu les controles i n'ets el propietari, no una mega-corporació que s'aprofita de les teves dades o les cedeix a tercers.
|
||||||
|
|
||||||
|
<b>MISSATGERIA I COL·LABORACIÓ OBERTA</b>: Pots parlar amb qualsevol que estigui a la xarxa Matrix, ja sigui amb Element o amb qualsevol altre aplicació Matrix, fins i tot encara que utilitzin sistemes de missatgeria diferents com Slack, IRC o XMPP.
|
||||||
|
|
||||||
|
<b>SUPER-SEGUR</b>: Encriptació d'extrem a extrem real (només qui està conversant pot desxifrar els missatges), i amb signatures creuades per a verificar els dispositius dels participants en les converses.
|
||||||
|
|
||||||
|
<b>COMUNICACIÓ COMPLETA</b>: Missatgeria, veu i video-trucades, compartició de fitxers, compartició de pantalla i un munt d'integracions, bots i ginys. Crea sales, comunitats, mantén-te en contacte i enllesteix el que et proposes.
|
||||||
|
|
||||||
|
<b>A TOT ARREU</b>: Mantingues el contacte des de qualsevol lloc on siguis, amb un historial de missatges totalment sincronitzat entre tots els teus dispositius i també a la web: https://app.element.io.
|
1
fastlane/metadata/android/ca/short_description.txt
Normal file
1
fastlane/metadata/android/ca/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Xat i VoIP segurs i descentralitzats. Protegeix les teves dades de tercers.
|
1
fastlane/metadata/android/ca/title.txt
Normal file
1
fastlane/metadata/android/ca/title.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Element (anteriorment Riot.im)
|
1
fastlane/metadata/android/de/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/de/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// TODO
|
2
fastlane/metadata/android/en-US/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40100110.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
This new version mainly contains user interface and user experience improvements. Now you can invite friends, and create DM very fast by scanning QR codes.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
1
fastlane/metadata/android/es/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/es/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// TODO
|
|
@ -1,30 +1,30 @@
|
||||||
Element es un nuevo tipo de aplicación de mensajería y colaboración que:
|
Element es un nuevo tipo de aplicación de mensajería y colaboración que:
|
||||||
|
|
||||||
1. Le da el control para preservar su privacidad
|
1. Te da el control para preservar su privacidad
|
||||||
2. Le permite comunicarse con cualquier persona en la red Matrix e incluso más allá al integrarse con aplicaciones como Slack.
|
2. Te permite comunicarse con cualquier persona en la red Matrix e incluso más allá al integrarse con aplicaciones como Slack
|
||||||
3. Te protege de la publicidad, la minería de datos y los jardines vallados.
|
3. Te protege de la publicidad, la minería de datos y los jardines vallados
|
||||||
4. Lo protege a través del cifrado de un extremo a otro, con firma cruzada para verificar a otros
|
4. Te protege a través de encriptación de Extremo-a-Extremo, con firma cruzada para verificar a otros
|
||||||
|
|
||||||
Element es completamente diferente de otras aplicaciones de mensajería y colaboración porque es descentralizado y de código abierto.
|
Element es completamente diferente de otras aplicaciones de mensajería y colaboración porque es descentralizado y de código abierto.
|
||||||
|
|
||||||
Element le permite autohospedarse, o elegir un host, para que tenga privacidad, propiedad y control de sus datos y conversaciones. Te da acceso a una red abierta; para que no se quede atascado hablando solo con otros usuarios de Element. Y es muy seguro.
|
Element te permite tener su propio servidor privado, o elegir uno público, para que tenga privacidad, posesión, y control de sus datos y conversaciones. Te da acceso a una red abierta; para que no se quede atrapado hablando solo con otros usuarios de Element. Y es muy seguro.
|
||||||
|
|
||||||
Element puede hacer todo esto porque opera en Matrix, el estándar para la comunicación abierta y descentralizada.
|
Element puede hacer todo esto porque opera en Matrix, el estándar para la comunicación abierta y descentralizada.
|
||||||
|
|
||||||
Element te da el control permitiéndote elegir quién aloja tus conversaciones. Desde la aplicación Element, puede elegir hospedar de diferentes maneras:
|
Element te da el control permitiéndote elegir quién aloja tus conversaciones. Desde la aplicación Element, puedes elegir hospedar de diferentes maneras:
|
||||||
|
|
||||||
1. Obtenga una cuenta gratuita en el servidor público de matrix.org alojado por los desarrolladores de Matrix, o elija entre miles de servidores públicos alojados por voluntarios
|
1. Obtén una cuenta gratuita en el servidor público de matrix.org alojado por los desarrolladores de Matrix, o elije entre miles de servidores públicos alojados por voluntarios
|
||||||
2. Autohospede su cuenta ejecutando un servidor en su propio hardware
|
2. Autohospeda tu cuenta con un servidor en tu propio hardware
|
||||||
3. Regístrese para obtener una cuenta en un servidor personalizado simplemente suscribiéndose a la plataforma de alojamiento de Element Matrix Services
|
3. Regístrate para obtener una cuenta en un servidor personalizado simplemente suscribiéndote a la plataforma de alojamiento de Element Matrix Services
|
||||||
|
|
||||||
<b>¿Por qué elegir Element?</b>
|
<b>¿Por qué elegir Element?</b>
|
||||||
|
|
||||||
<b>POSEE SUS DATOS</b>: Tú decides dónde guardar tus datos y mensajes. Usted es el propietario y lo controla, no algún MEGACORP que extraiga sus datos o dé acceso a terceros.
|
<b>TOMA POSESIÓN DE TUS DATOS</b>: Tú decides dónde guardar tus datos y mensajes. Tú eres el propietario y quien lo controla, no alguna MEGACORP que extrae tu datos o da acceso a terceros.
|
||||||
|
|
||||||
<b>MENSAJERÍA ABIERTA Y COLABORACIÓN</b>: Puede chatear con cualquier otra persona en la red de Matrix, ya sea que estén usando Element u otra aplicación de Matrix, e incluso si están usando un sistema de mensajería diferente como Slack, IRC o XMPP.
|
<b>MENSAJERÍA ABIERTA Y COLABORACIÓN</b>: Puede chatear con cualquier otra persona en la red de Matrix, tanto si usan Element u otra aplicación de Matrix, e incluso si están usando un sistema de mensajería diferente como Slack, IRC o XMPP.
|
||||||
|
|
||||||
<b>SUPER SEGURO</b>: Cifrado real de extremo a extremo (solo aquellos en la conversación pueden descifrar mensajes) y firma cruzada para verificar los dispositivos de los participantes de la conversación.
|
<b>SUPER SEGURO</b>: Encriptación de Extremo-a-Extremo real (solo aquellos en la conversación pueden descifrar mensajes) y firma cruzada para verificar los dispositivos de los participantes de la conversación.
|
||||||
|
|
||||||
<b>COMUNICACIÓN COMPLETA</b>: Mensajería, llamadas de voz y video, uso compartido de archivos, uso compartido de pantalla y un montón de integraciones, bots y widgets. Construya salas, comunidades, manténgase en contacto y haga las cosas.
|
<b>COMUNICACIÓN COMPLETA</b>: Mensajería, llamadas de voz y video, uso compartido de archivos, uso compartido de pantalla y un montón de integraciones, bots y widgets. Crea salas, comunidades, mantente en contacto y organízate con eficacia.
|
||||||
|
|
||||||
<b>EN TODAS PARTES</b>: Manténgase en contacto donde quiera que esté con un historial de mensajes totalmente sincronizado en todos sus dispositivos y en la web en https://app.element.io.
|
<b>EN TODAS PARTES</b>: Mantente en contacto donde quiera que estés con un historial de mensajes totalmente sincronizado en todos sus dispositivos y en la web en https://app.element.io.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Chat y VoIP descentralizados seguros. Mantenga sus datos a salvo de terceros.
|
Chat y VoIP descentralizados y seguros. Mantén tus datos a salvo de terceros.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Element (anteriorment Riot.im)
|
Element (previamente Riot.im)
|
||||||
|
|
1
fastlane/metadata/android/fa/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/fa/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// برای انجام
|
1
fastlane/metadata/android/it/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/it/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// DA FARE
|
1
fastlane/metadata/android/nb/short_description.txt
Normal file
1
fastlane/metadata/android/nb/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Sikker desentralisert chat & VoIP. Beskytt dataene dine fra tredjeparter.
|
1
fastlane/metadata/android/nb/title.txt
Normal file
1
fastlane/metadata/android/nb/title.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Element (tidligere Riot.im)
|
1
fastlane/metadata/android/pt_BR/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/pt_BR/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// A FAZER
|
1
fastlane/metadata/android/sv/changelogs/40100100.txt
Normal file
1
fastlane/metadata/android/sv/changelogs/40100100.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// ATT GÖRA
|
|
@ -0,0 +1 @@
|
||||||
|
// 待辦事項
|
30
fastlane/metadata/android/zh_Hant/full_description.txt
Normal file
30
fastlane/metadata/android/zh_Hant/full_description.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Element 是一種新型態的即時通訊軟體與協作應用程式:
|
||||||
|
|
||||||
|
1. 自己的隱私自己掌控
|
||||||
|
2. 讓您與任何在 Matrix 網路中的人通訊,甚至可與如 Slack 等的應用程式整合
|
||||||
|
3. 保護您免受廣告、資料採礦與圍牆花園的侵害
|
||||||
|
4. 透過端到端加密保護您,並使用交叉簽章來驗證其他人
|
||||||
|
|
||||||
|
Element 是去中心化且開放原始碼的應用程式,因此與其他即時通訊與協作軟體完全不同。
|
||||||
|
|
||||||
|
Element 讓您可以自架(或是自行選擇服務提供者)所以您擁有您資料與對話的隱私、所有權與控制權。它讓您可以存取開放的網路;因此,您不僅可以與其他 Matrix 使用者聊天。而且非常安全。
|
||||||
|
|
||||||
|
Element 能作到這些事情是因為它在 Matrix 上執行,這是一個開放的去中心化通訊的標準。
|
||||||
|
|
||||||
|
Element 讓您選擇您要在哪裡託管您的對話來將控制權還給您。在 Element 應用程式中,您可以選擇其他方式來託管:
|
||||||
|
|
||||||
|
1. 在由 Matrix 開發者架設的 matrix.org 公開伺服器上取得免費的帳號,或是從數千個由志願者所架設的公開伺服器中選擇
|
||||||
|
2. 在您自己的硬體上自行架設伺服器並建立帳號
|
||||||
|
3. 訂閱 Element Matrix 服務託管平台並在自訂伺服氣上註冊帳號
|
||||||
|
|
||||||
|
<b>為何選擇 Element?</b>
|
||||||
|
|
||||||
|
<b>擁有您的資料</b>:您決定您的資料與訊息要放在哪裡。您擁有並控制它,而非某些科技巨頭會挖掘您的資料並將其售予第三方。
|
||||||
|
|
||||||
|
<b>開放的即時通訊與協作</b>:您可以與 Matrix 網路中的任何人聊天,不管他們是使用 Element 或其他 Matrix 應用程式都可以,或甚至是其他的訊息系統,如 Slack、IRC 或 XMPP 也都可以。
|
||||||
|
|
||||||
|
<b>超級安全</b>:即時的端到端加密(僅有參與對話的人可以解密訊息),以及交叉簽章以驗證對話參與者的裝置。
|
||||||
|
|
||||||
|
<b>完整通訊</b>:即時通訊、語音與視訊通話、檔案分享、畫面分享與超多的整合、機器人與小工具。建立聊天室、保持聯繫並完成工作。
|
||||||
|
|
||||||
|
<b>無論您身在何處</b>:無論您身在何處,都可以透過 https://app.element.io 來在所有裝置與網路上保持訊息歷史同步。
|
1
fastlane/metadata/android/zh_Hant/short_description.txt
Normal file
1
fastlane/metadata/android/zh_Hant/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
安全的去中心化聊天與 VoIP。確保您的資料不受第三方的影響。
|
1
fastlane/metadata/android/zh_Hant/title.txt
Normal file
1
fastlane/metadata/android/zh_Hant/title.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Element(曾名為 Riot.im)
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
|
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.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||||
|
@ -92,6 +93,13 @@ class RxSession(private val session: Session) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveRoomMember(userId: String, roomId: String): Observable<Optional<RoomMemberSummary>> {
|
||||||
|
return session.getRoomMemberLive(userId, roomId).asObservable()
|
||||||
|
.startWithCallable {
|
||||||
|
session.getRoomMember(userId, roomId).toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun liveUsers(): Observable<List<User>> {
|
fun liveUsers(): Observable<List<User>> {
|
||||||
return session.getUsersLive().asObservable()
|
return session.getUsersLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "io.realm:realm-gradle-plugin:6.1.0"
|
classpath "io.realm:realm-gradle-plugin:10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ dependencies {
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
implementation 'androidx.exifinterface:exifinterface:1.3.0'
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||||
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
|
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
|
||||||
|
|
||||||
// Work
|
// Work
|
||||||
|
|
|
@ -43,8 +43,8 @@ class ChangePasswordTest : InstrumentedTest {
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
||||||
|
|
||||||
// Change password
|
// Change password
|
||||||
commonTestHelper.doSync<Unit> {
|
commonTestHelper.runBlockingTest {
|
||||||
session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD, it)
|
session.changePassword(TestConstants.PASSWORD, NEW_PASSWORD)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to login with the previous password, it will fail
|
// Try to login with the previous password, it will fail
|
||||||
|
|
|
@ -43,8 +43,8 @@ class DeactivateAccountTest : InstrumentedTest {
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
||||||
|
|
||||||
// Deactivate the account
|
// Deactivate the account
|
||||||
commonTestHelper.doSync<Unit> {
|
commonTestHelper.runBlockingTest {
|
||||||
session.deactivateAccount(TestConstants.PASSWORD, false, it)
|
session.deactivateAccount(TestConstants.PASSWORD, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
|
// Try to login on the previous account, it will fail (M_USER_DEACTIVATED)
|
||||||
|
|
|
@ -40,6 +40,7 @@ import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
|
@ -343,6 +344,14 @@ class CommonTestHelper(context: Context) {
|
||||||
await(latch, timeout)
|
await(latch, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> runBlockingTest(timeout: Long = TestConstants.timeOutMillis, block: suspend () -> T): T {
|
||||||
|
return runBlocking {
|
||||||
|
withTimeout(timeout) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Transform a method with a MatrixCallback to a synchronous method
|
// Transform a method with a MatrixCallback to a synchronous method
|
||||||
inline fun <reified T> doSync(timeout: Long? = TestConstants.timeOutMillis, block: (MatrixCallback<T>) -> Unit): T {
|
inline fun <reified T> doSync(timeout: Long? = TestConstants.timeOutMillis, block: (MatrixCallback<T>) -> Unit): T {
|
||||||
val lock = CountDownLatch(1)
|
val lock = CountDownLatch(1)
|
||||||
|
|
|
@ -68,8 +68,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
|
||||||
if (encryptedRoom) {
|
if (encryptedRoom) {
|
||||||
val room = aliceSession.getRoom(roomId)!!
|
val room = aliceSession.getRoom(roomId)!!
|
||||||
|
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.runBlockingTest {
|
||||||
room.enableEncryption(callback = it)
|
room.enableEncryption()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -555,7 +555,7 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
requestID = prAlicePOV?.transactionId
|
requestID = prAlicePOV?.transactionId
|
||||||
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
|
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
|
||||||
|
@ -566,7 +566,7 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prBobPOV = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId)?.firstOrNull()
|
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
||||||
prBobPOV?.transactionId == requestID
|
prBobPOV?.transactionId == requestID
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ class SASTest : InstrumentedTest {
|
||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
commonTestHelper.await(lock)
|
commonTestHelper.await(lock)
|
||||||
|
|
||||||
lock = CountDownLatch(1)
|
lock = CountDownLatch(1)
|
||||||
|
val data = commonTestHelper.runBlockingTest {
|
||||||
aliceSession
|
aliceSession
|
||||||
.searchService()
|
.searchService()
|
||||||
.search(
|
.search(
|
||||||
|
@ -81,10 +82,9 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
beforeLimit = 10,
|
beforeLimit = 10,
|
||||||
orderByRecent = true,
|
orderByRecent = true,
|
||||||
nextBatch = null,
|
nextBatch = null,
|
||||||
roomId = aliceRoomId,
|
roomId = aliceRoomId
|
||||||
callback = object : MatrixCallback<SearchResult> {
|
)
|
||||||
override fun onSuccess(data: SearchResult) {
|
}
|
||||||
super.onSuccess(data)
|
|
||||||
assertTrue(data.results?.size == 2)
|
assertTrue(data.results?.size == 2)
|
||||||
assertTrue(
|
assertTrue(
|
||||||
data.results
|
data.results
|
||||||
|
@ -92,17 +92,6 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
(it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse()
|
(it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse()
|
||||||
}.orFalse()
|
}.orFalse()
|
||||||
)
|
)
|
||||||
lock.countDown()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
super.onFailure(failure)
|
|
||||||
fail(failure.localizedMessage)
|
|
||||||
lock.countDown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
lock.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)
|
|
||||||
|
|
||||||
aliceTimeline.removeAllListeners()
|
aliceTimeline.removeAllListeners()
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
cryptoTestData.cleanUp(commonTestHelper)
|
||||||
|
|
|
@ -66,9 +66,9 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logJson(formattedJson: String) {
|
private fun logJson(formattedJson: String) {
|
||||||
val arr = formattedJson.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
formattedJson
|
||||||
for (s in arr) {
|
.lines()
|
||||||
Timber.v(s)
|
.dropLastWhile { it.isEmpty() }
|
||||||
}
|
.forEach { Timber.v(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ interface LoginWizard {
|
||||||
* @param password the password field
|
* @param password the password field
|
||||||
* @param deviceName the initial device name
|
* @param deviceName the initial device name
|
||||||
* @param callback the matrix callback on which you'll receive the result of authentication.
|
* @param callback the matrix callback on which you'll receive the result of authentication.
|
||||||
* @return return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun login(login: String,
|
fun login(login: String,
|
||||||
password: String,
|
password: String,
|
||||||
|
|
|
@ -22,3 +22,8 @@ fun CharSequence.ensurePrefix(prefix: CharSequence): CharSequence {
|
||||||
else -> "$prefix$this"
|
else -> "$prefix$this"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a new line and then the provided string
|
||||||
|
*/
|
||||||
|
fun StringBuilder.appendNl(str: String) = append("\n").append(str)
|
||||||
|
|
|
@ -15,11 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.matrix.android.sdk.api.pushrules
|
package org.matrix.android.sdk.api.pushrules
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||||
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
|
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
interface PushRuleService {
|
interface PushRuleService {
|
||||||
/**
|
/**
|
||||||
|
@ -29,13 +27,13 @@ interface PushRuleService {
|
||||||
|
|
||||||
fun getPushRules(scope: String = RuleScope.GLOBAL): RuleSet
|
fun getPushRules(scope: String = RuleScope.GLOBAL): RuleSet
|
||||||
|
|
||||||
fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean)
|
||||||
|
|
||||||
fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun addPushRule(kind: RuleKind, pushRule: PushRule)
|
||||||
|
|
||||||
fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule)
|
||||||
|
|
||||||
fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun removePushRule(kind: RuleKind, pushRule: PushRule)
|
||||||
|
|
||||||
fun addPushRuleListener(listener: PushRuleListener)
|
fun addPushRuleListener(listener: PushRuleListener)
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.raw
|
package org.matrix.android.sdk.api.raw
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Useful methods to fetch raw data from the server. The access token will not be used to fetched the data
|
* Useful methods to fetch raw data from the server. The access token will not be used to fetched the data
|
||||||
*/
|
*/
|
||||||
|
@ -26,17 +23,15 @@ interface RawService {
|
||||||
/**
|
/**
|
||||||
* Get a URL, either from cache or from the remote server, depending on the cache strategy
|
* Get a URL, either from cache or from the remote server, depending on the cache strategy
|
||||||
*/
|
*/
|
||||||
fun getUrl(url: String,
|
suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String
|
||||||
rawCacheStrategy: RawCacheStrategy,
|
|
||||||
matrixCallback: MatrixCallback<String>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific case for the well-known file. Cache validity is 8 hours
|
* Specific case for the well-known file. Cache validity is 8 hours
|
||||||
*/
|
*/
|
||||||
fun getWellknown(userId: String, matrixCallback: MatrixCallback<String>): Cancelable
|
suspend fun getWellknown(userId: String): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all the cache data
|
* Clear all the cache data
|
||||||
*/
|
*/
|
||||||
fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable
|
suspend fun clearCache()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.account
|
package org.matrix.android.sdk.api.session.account
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to manage the account. It's implemented at the session level.
|
* This interface defines methods to manage the account. It's implemented at the session level.
|
||||||
*/
|
*/
|
||||||
|
@ -28,7 +25,7 @@ interface AccountService {
|
||||||
* @param password Current password.
|
* @param password Current password.
|
||||||
* @param newPassword New password
|
* @param newPassword New password
|
||||||
*/
|
*/
|
||||||
fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun changePassword(password: String, newPassword: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deactivate the account.
|
* Deactivate the account.
|
||||||
|
@ -46,5 +43,5 @@ interface AccountService {
|
||||||
* @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see
|
* @param eraseAllData set to true to forget all messages that have been sent. Warning: this will cause future users to see
|
||||||
* an incomplete view of conversations
|
* an incomplete view of conversations
|
||||||
*/
|
*/
|
||||||
fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun deactivateAccount(password: String, eraseAllData: Boolean)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ interface VerificationService {
|
||||||
|
|
||||||
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
|
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>?
|
fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest>
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
|
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ object EventType {
|
||||||
const val STATE_ROOM_RELATED_GROUPS = "m.room.related_groups"
|
const val STATE_ROOM_RELATED_GROUPS = "m.room.related_groups"
|
||||||
const val STATE_ROOM_PINNED_EVENT = "m.room.pinned_events"
|
const val STATE_ROOM_PINNED_EVENT = "m.room.pinned_events"
|
||||||
const val STATE_ROOM_ENCRYPTION = "m.room.encryption"
|
const val STATE_ROOM_ENCRYPTION = "m.room.encryption"
|
||||||
|
const val STATE_ROOM_SERVER_ACL = "m.room.server_acl"
|
||||||
|
|
||||||
// Call Events
|
// Call Events
|
||||||
const val CALL_INVITE = "m.call.invite"
|
const val CALL_INVITE = "m.call.invite"
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.group
|
package org.matrix.android.sdk.api.session.group
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to interact within a group.
|
* This interface defines methods to interact within a group.
|
||||||
*/
|
*/
|
||||||
|
@ -28,8 +25,7 @@ interface Group {
|
||||||
/**
|
/**
|
||||||
* This methods allows you to refresh data about this group. It will be reflected on the GroupSummary.
|
* This methods allows you to refresh data about this group. It will be reflected on the GroupSummary.
|
||||||
* The SDK also takes care of refreshing group data every hour.
|
* The SDK also takes care of refreshing group data every hour.
|
||||||
* @param callback : the matrix callback to be notified of success or failure
|
|
||||||
* @return a Cancelable to be able to cancel requests.
|
* @return a Cancelable to be able to cancel requests.
|
||||||
*/
|
*/
|
||||||
fun fetchGroupData(callback: MatrixCallback<Unit>): Cancelable
|
suspend fun fetchGroupData()
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,9 +92,29 @@ interface IdentityService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search MatrixId of users providing email and phone numbers
|
* Search MatrixId of users providing email and phone numbers
|
||||||
|
* Note the the user consent has to be set to true, or it will throw a UserConsentNotProvided failure
|
||||||
|
* Application has to explicitly ask for the user consent, and the answer can be stored using [setUserConsent]
|
||||||
|
* Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details.
|
||||||
*/
|
*/
|
||||||
fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable
|
fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current user consent for the current identity server, which has been stored using [setUserConsent].
|
||||||
|
* If [setUserConsent] has not been called, the returned value will be false.
|
||||||
|
* Note that if the identity server is changed, the user consent is reset to false.
|
||||||
|
* @return the value stored using [setUserConsent] or false if [setUserConsent] has never been called, or if the identity server
|
||||||
|
* has been changed
|
||||||
|
*/
|
||||||
|
fun getUserConsent(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the user consent to the provided value. Application MUST explicitly ask for the user consent to send their private data
|
||||||
|
* (email and phone numbers) to the identity server.
|
||||||
|
* Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details.
|
||||||
|
* @param newValue true if the user explicitly give their consent, false if the user wants to revoke their consent.
|
||||||
|
*/
|
||||||
|
fun setUserConsent(newValue: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the status of the current user's threePid
|
* Get the status of the current user's threePid
|
||||||
* A lookup will be performed, but also pending binding state will be restored
|
* A lookup will be performed, but also pending binding state will be restored
|
||||||
|
|
|
@ -24,6 +24,7 @@ sealed class IdentityServiceError : Failure.FeatureFailure() {
|
||||||
object NoIdentityServerConfigured : IdentityServiceError()
|
object NoIdentityServerConfigured : IdentityServiceError()
|
||||||
object TermsNotSignedException : IdentityServiceError()
|
object TermsNotSignedException : IdentityServiceError()
|
||||||
object BulkLookupSha256NotSupported : IdentityServiceError()
|
object BulkLookupSha256NotSupported : IdentityServiceError()
|
||||||
|
object UserConsentNotProvided : IdentityServiceError()
|
||||||
object BindingError : IdentityServiceError()
|
object BindingError : IdentityServiceError()
|
||||||
object NoCurrentBindingError : IdentityServiceError()
|
object NoCurrentBindingError : IdentityServiceError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.matrix.android.sdk.api.session.permalinks
|
package org.matrix.android.sdk.api.session.permalinks
|
||||||
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
|
import org.matrix.android.sdk.api.MatrixPatterns
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MatrixLinkify take a piece of text and turns all of the
|
* MatrixLinkify take a piece of text and turns all of the
|
||||||
|
@ -35,7 +36,7 @@ object MatrixLinkify {
|
||||||
* I disable it because it mess up with pills, and even with pills, it does not work correctly:
|
* I disable it because it mess up with pills, and even with pills, it does not work correctly:
|
||||||
* The url is not correct. Ex: for @user:matrix.org, the url will be @user:matrix.org, instead of a matrix.to
|
* The url is not correct. Ex: for @user:matrix.org, the url will be @user:matrix.org, instead of a matrix.to
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (spannable.isEmpty()) {
|
if (spannable.isEmpty()) {
|
||||||
return false
|
return false
|
||||||
|
@ -48,14 +49,21 @@ object MatrixLinkify {
|
||||||
val startPos = match.range.first
|
val startPos = match.range.first
|
||||||
if (startPos == 0 || text[startPos - 1] != '/') {
|
if (startPos == 0 || text[startPos - 1] != '/') {
|
||||||
val endPos = match.range.last + 1
|
val endPos = match.range.last + 1
|
||||||
val url = text.substring(match.range)
|
var url = text.substring(match.range)
|
||||||
|
if (MatrixPatterns.isUserId(url)
|
||||||
|
|| MatrixPatterns.isRoomAlias(url)
|
||||||
|
|| MatrixPatterns.isRoomId(url)
|
||||||
|
|| MatrixPatterns.isGroupId(url)
|
||||||
|
|| MatrixPatterns.isEventId(url)) {
|
||||||
|
url = PermalinkService.MATRIX_TO_URL_BASE + url
|
||||||
|
}
|
||||||
val span = MatrixPermalinkSpan(url, callback)
|
val span = MatrixPermalinkSpan(url, callback)
|
||||||
spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hasMatch
|
return hasMatch
|
||||||
*/
|
|
||||||
return false
|
// return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,13 +44,12 @@ object PermalinkParser {
|
||||||
if (fragment.isNullOrEmpty()) {
|
if (fragment.isNullOrEmpty()) {
|
||||||
return PermalinkData.FallbackLink(uri)
|
return PermalinkData.FallbackLink(uri)
|
||||||
}
|
}
|
||||||
val indexOfQuery = fragment.indexOf("?")
|
val safeFragment = fragment.substringBefore('?')
|
||||||
val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment
|
|
||||||
val viaQueryParameters = fragment.getViaParameters()
|
val viaQueryParameters = fragment.getViaParameters()
|
||||||
|
|
||||||
// we are limiting to 2 params
|
// we are limiting to 2 params
|
||||||
val params = safeFragment
|
val params = safeFragment
|
||||||
.split(MatrixPatterns.SEP_REGEX.toRegex())
|
.split(MatrixPatterns.SEP_REGEX)
|
||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
.take(2)
|
.take(2)
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session.room
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
|
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.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
@ -141,4 +142,20 @@ interface RoomService {
|
||||||
* - the power level of the users are not taken into account. Normally in a DM, the 2 members are admins of the room
|
* - the power level of the users are not taken into account. Normally in a DM, the 2 members are admins of the room
|
||||||
*/
|
*/
|
||||||
fun getExistingDirectRoomWithUser(otherUserId: String): String?
|
fun getExistingDirectRoomWithUser(otherUserId: String): String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a room member for the tuple {userId,roomId}
|
||||||
|
* @param userId the userId to look for.
|
||||||
|
* @param roomId the roomId to look for.
|
||||||
|
* @return the room member or null
|
||||||
|
*/
|
||||||
|
fun getRoomMember(userId: String, roomId: String): RoomMemberSummary?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe a live room member for the tuple {userId,roomId}
|
||||||
|
* @param userId the userId to look for.
|
||||||
|
* @param roomId the roomId to look for.
|
||||||
|
* @return a LiveData of the optional found room member
|
||||||
|
*/
|
||||||
|
fun getRoomMemberLive(userId: String, roomId: String): LiveData<Optional<RoomMemberSummary>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.crypto
|
package org.matrix.android.sdk.api.session.room.crypto
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
|
||||||
interface RoomCryptoService {
|
interface RoomCryptoService {
|
||||||
|
@ -30,6 +29,5 @@ interface RoomCryptoService {
|
||||||
/**
|
/**
|
||||||
* Enable encryption of the room
|
* Enable encryption of the room
|
||||||
*/
|
*/
|
||||||
fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM,
|
suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM)
|
||||||
callback: MatrixCallback<Unit>)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,9 @@ import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
sealed class CreateRoomFailure : Failure.FeatureFailure() {
|
sealed class CreateRoomFailure : Failure.FeatureFailure() {
|
||||||
object CreatedWithTimeout : CreateRoomFailure()
|
object CreatedWithTimeout : CreateRoomFailure()
|
||||||
data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure()
|
data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure()
|
||||||
|
sealed class RoomAliasError : CreateRoomFailure() {
|
||||||
|
object AliasEmpty : RoomAliasError()
|
||||||
|
object AliasNotAvailable : RoomAliasError()
|
||||||
|
object AliasInvalid : RoomAliasError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.session.room.model
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing the EventType.STATE_ROOM_SERVER_ACL state event content
|
||||||
|
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class RoomServerAclContent(
|
||||||
|
/**
|
||||||
|
* True to allow server names that are IP address literals. False to deny.
|
||||||
|
* Defaults to true if missing or otherwise not a boolean.
|
||||||
|
* This is strongly recommended to be set to false as servers running with IP literal names are strongly
|
||||||
|
* discouraged in order to require legitimate homeservers to be backed by a valid registered domain name.
|
||||||
|
*/
|
||||||
|
@Json(name = "allow_ip_literals")
|
||||||
|
val allowIpLiterals: Boolean = true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server names to allow in the room, excluding any port information. Wildcards may be used to cover
|
||||||
|
* a wider range of hosts, where * matches zero or more characters and ? matches exactly one character.
|
||||||
|
*
|
||||||
|
* This defaults to an empty list when not provided, effectively disallowing every server.
|
||||||
|
*/
|
||||||
|
@Json(name = "allow")
|
||||||
|
val allowList: List<String> = emptyList(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server names to disallow in the room, excluding any port information. Wildcards may be used to cover
|
||||||
|
* a wider range of hosts, where * matches zero or more characters and ? matches exactly one character.
|
||||||
|
*
|
||||||
|
* This defaults to an empty list when not provided.
|
||||||
|
*/
|
||||||
|
@Json(name = "deny")
|
||||||
|
val denyList: List<String> = emptyList()
|
||||||
|
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
const val ALL = "*"
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,7 +94,22 @@ class CreateRoomParams {
|
||||||
* The server will clobber the following keys: creator.
|
* The server will clobber the following keys: creator.
|
||||||
* Future versions of the specification may allow the server to clobber other keys.
|
* Future versions of the specification may allow the server to clobber other keys.
|
||||||
*/
|
*/
|
||||||
var creationContent: Any? = null
|
val creationContent = mutableMapOf<String, Any>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to disable federation of this room.
|
||||||
|
* Default: false
|
||||||
|
*/
|
||||||
|
var disableFederation = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
if (value) {
|
||||||
|
creationContent[CREATION_CONTENT_KEY_M_FEDERATE] = false
|
||||||
|
} else {
|
||||||
|
// This is the default value, we remove the field
|
||||||
|
creationContent.remove(CREATION_CONTENT_KEY_M_FEDERATE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The power level content to override in the default power level event
|
* The power level content to override in the default power level event
|
||||||
|
@ -120,4 +135,8 @@ class CreateRoomParams {
|
||||||
fun enableEncryption() {
|
fun enableEncryption() {
|
||||||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,10 @@
|
||||||
package org.matrix.android.sdk.api.session.room.notification
|
package org.matrix.android.sdk.api.session.room.notification
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
interface RoomPushRuleService {
|
interface RoomPushRuleService {
|
||||||
|
|
||||||
fun getLiveRoomNotificationState(): LiveData<RoomNotificationState>
|
fun getLiveRoomNotificationState(): LiveData<RoomNotificationState>
|
||||||
|
|
||||||
fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable
|
suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.reporting
|
package org.matrix.android.sdk.api.session.room.reporting
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to report content of an event.
|
* This interface defines methods to report content of an event.
|
||||||
*/
|
*/
|
||||||
|
@ -28,5 +25,5 @@ interface ReportingService {
|
||||||
* Report content
|
* Report content
|
||||||
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid
|
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-rooms-roomid-report-eventid
|
||||||
*/
|
*/
|
||||||
fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun reportContent(eventId: String, score: Int, reason: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
package org.matrix.android.sdk.api.session.room.send
|
package org.matrix.android.sdk.api.session.room.send
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
interface DraftService {
|
interface DraftService {
|
||||||
|
@ -26,12 +24,12 @@ interface DraftService {
|
||||||
/**
|
/**
|
||||||
* Save or update a draft to the room
|
* Save or update a draft to the room
|
||||||
*/
|
*/
|
||||||
fun saveDraft(draft: UserDraft, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun saveDraft(draft: UserDraft)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the last draft, basically just after sending the message
|
* Delete the last draft, basically just after sending the message
|
||||||
*/
|
*/
|
||||||
fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable
|
suspend fun deleteDraft()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current draft or null
|
* Return the current draft or null
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.tags
|
package org.matrix.android.sdk.api.session.room.tags
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to handle tags of a room. It's implemented at the room level.
|
* This interface defines methods to handle tags of a room. It's implemented at the room level.
|
||||||
*/
|
*/
|
||||||
|
@ -26,10 +23,10 @@ interface TagsService {
|
||||||
/**
|
/**
|
||||||
* Add a tag to a room
|
* Add a tag to a room
|
||||||
*/
|
*/
|
||||||
fun addTag(tag: String, order: Double?, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun addTag(tag: String, order: Double?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove tag from a room
|
* Remove tag from a room
|
||||||
*/
|
*/
|
||||||
fun deleteTag(tag: String, callback: MatrixCallback<Unit>): Cancelable
|
suspend fun deleteTag(tag: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.search
|
package org.matrix.android.sdk.api.session.search
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to search messages in rooms.
|
* This interface defines methods to search messages in rooms.
|
||||||
*/
|
*/
|
||||||
|
@ -35,15 +32,13 @@ interface SearchService {
|
||||||
* @param beforeLimit how many events before the result are returned.
|
* @param beforeLimit how many events before the result are returned.
|
||||||
* @param afterLimit how many events after the result are returned.
|
* @param afterLimit how many events after the result are returned.
|
||||||
* @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
|
* @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
|
||||||
* @param callback Callback to get the search result
|
|
||||||
*/
|
*/
|
||||||
fun search(searchTerm: String,
|
suspend fun search(searchTerm: String,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
nextBatch: String?,
|
nextBatch: String?,
|
||||||
orderByRecent: Boolean,
|
orderByRecent: Boolean,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
beforeLimit: Int,
|
beforeLimit: Int,
|
||||||
afterLimit: Int,
|
afterLimit: Int,
|
||||||
includeProfile: Boolean,
|
includeProfile: Boolean): SearchResult
|
||||||
callback: MatrixCallback<SearchResult>): Cancelable
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ interface UserService {
|
||||||
*/
|
*/
|
||||||
fun getUser(userId: String): User?
|
fun getUser(userId: String): User?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to resolve user from known users, or using profile api
|
||||||
|
*/
|
||||||
|
fun resolveUser(userId: String, callback: MatrixCallback<User>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search list of users on server directory.
|
* Search list of users on server directory.
|
||||||
* @param search the searched term
|
* @param search the searched term
|
||||||
|
|
|
@ -123,6 +123,7 @@ internal abstract class CryptoModule {
|
||||||
}
|
}
|
||||||
.name("crypto_store.realm")
|
.name("crypto_store.realm")
|
||||||
.modules(RealmCryptoStoreModule())
|
.modules(RealmCryptoStoreModule())
|
||||||
|
.allowWritesOnUiThread(true)
|
||||||
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
|
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)
|
||||||
.migration(realmCryptoStoreMigration)
|
.migration(realmCryptoStoreMigration)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -767,9 +767,9 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
*/
|
*/
|
||||||
private fun onRoomKeyEvent(event: Event) {
|
private fun onRoomKeyEvent(event: Event) {
|
||||||
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
|
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
|
||||||
Timber.v("## CRYPTO | GOSSIP onRoomKeyEvent() : type<${event.getClearType()}> , sessionId<${roomKeyContent.sessionId}>")
|
Timber.i("## CRYPTO | onRoomKeyEvent() from: ${event.senderId} type<${event.getClearType()}> , sessionId<${roomKeyContent.sessionId}>")
|
||||||
if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.algorithm.isNullOrEmpty()) {
|
if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.algorithm.isNullOrEmpty()) {
|
||||||
Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : missing fields")
|
Timber.e("## CRYPTO | onRoomKeyEvent() : missing fields")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
|
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
|
||||||
|
@ -782,20 +782,20 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
|
|
||||||
private fun onKeyWithHeldReceived(event: Event) {
|
private fun onKeyWithHeldReceived(event: Event) {
|
||||||
val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also {
|
val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also {
|
||||||
Timber.e("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
|
Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
|
||||||
}
|
}
|
||||||
Timber.d("## CRYPTO | onKeyWithHeldReceived() received : content <$withHeldContent>")
|
Timber.i("## CRYPTO | onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>")
|
||||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm)
|
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm)
|
||||||
if (alg is IMXWithHeldExtension) {
|
if (alg is IMXWithHeldExtension) {
|
||||||
alg.onRoomKeyWithHeldEvent(withHeldContent)
|
alg.onRoomKeyWithHeldEvent(withHeldContent)
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## CRYPTO | onKeyWithHeldReceived() : Unable to handle WithHeldContent for ${withHeldContent.algorithm}")
|
Timber.e("## CRYPTO | onKeyWithHeldReceived() from:${event.senderId}: Unable to handle WithHeldContent for ${withHeldContent.algorithm}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSecretSendReceived(event: Event) {
|
private fun onSecretSendReceived(event: Event) {
|
||||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() : onSecretSendReceived ${event.content?.get("sender_key")}")
|
Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||||
if (!event.isEncrypted()) {
|
if (!event.isEncrypted()) {
|
||||||
// secret send messages must be encrypted
|
// secret send messages must be encrypted
|
||||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() :Received unencrypted secret send event")
|
Timber.e("## CRYPTO | GOSSIP onSecretSend() :Received unencrypted secret send event")
|
||||||
|
|
|
@ -119,7 +119,7 @@ internal class EventDecryptor @Inject constructor(
|
||||||
markOlmSessionForUnwedging(event.senderId ?: "", it)
|
markOlmSessionForUnwedging(event.senderId ?: "", it)
|
||||||
}
|
}
|
||||||
?: run {
|
?: run {
|
||||||
Timber.v("## CRYPTO | markOlmSessionForUnwedging() : Failed to find sender crypto device")
|
Timber.i("## CRYPTO | internalDecryptEvent() : Failed to find sender crypto device for unwedging")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,16 +137,18 @@ internal class EventDecryptor @Inject constructor(
|
||||||
val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||||
Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
Timber.w("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
Timber.i("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
||||||
lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
|
lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
|
||||||
|
|
||||||
// offload this from crypto thread (?)
|
// offload this from crypto thread (?)
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.computation) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.computation) {
|
||||||
ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true)
|
val ensured = ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true)
|
||||||
|
|
||||||
|
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : ensureOlmSessionsForDevicesAction isEmpty:${ensured.isEmpty}")
|
||||||
|
|
||||||
// Now send a blank message on that session so the other side knows about it.
|
// Now send a blank message on that session so the other side knows about it.
|
||||||
// (The keyshare request is sent in the clear so that won't do)
|
// (The keyshare request is sent in the clear so that won't do)
|
||||||
|
@ -159,10 +161,14 @@ internal class EventDecryptor @Inject constructor(
|
||||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||||
sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
|
sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
|
||||||
Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}")
|
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : sending dummy to $senderId:${deviceInfo.deviceId}")
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||||
|
try {
|
||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.e(failure, "## CRYPTO | markOlmSessionForUnwedging() : failed to send dummy to $senderId:${deviceInfo.deviceId}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
private val cryptoCoroutineScope: CoroutineScope) {
|
private val cryptoCoroutineScope: CoroutineScope) {
|
||||||
|
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
// list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations
|
// list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations
|
||||||
// we received in the current sync.
|
// we received in the current sync.
|
||||||
private val receivedGossipingRequests = ArrayList<IncomingShareRequestCommon>()
|
private val receivedGossipingRequests = ArrayList<IncomingShareRequestCommon>()
|
||||||
|
@ -103,8 +104,8 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
* @param event the announcement event.
|
* @param event the announcement event.
|
||||||
*/
|
*/
|
||||||
fun onGossipingRequestEvent(event: Event) {
|
fun onGossipingRequestEvent(event: Event) {
|
||||||
Timber.v("## CRYPTO | GOSSIP onGossipingRequestEvent type ${event.type} from user ${event.senderId}")
|
|
||||||
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
|
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
|
||||||
|
Timber.i("## CRYPTO | GOSSIP onGossipingRequestEvent received type ${event.type} from user:${event.senderId}, content:$roomKeyShare")
|
||||||
// val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
// val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||||
when (roomKeyShare?.action) {
|
when (roomKeyShare?.action) {
|
||||||
GossipingToDeviceObject.ACTION_SHARE_REQUEST -> {
|
GossipingToDeviceObject.ACTION_SHARE_REQUEST -> {
|
||||||
|
|
|
@ -760,7 +760,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
|
Timber.w("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ internal class SendGossipWorker(context: Context,
|
||||||
requestId = params.requestId,
|
requestId = params.requestId,
|
||||||
state = GossipingRequestState.FAILED_TO_ACCEPTED
|
state = GossipingRequestState.FAILED_TO_ACCEPTED
|
||||||
)
|
)
|
||||||
Timber.e("no session with this device, probably because there were no one-time keys.")
|
Timber.e("no session with this device $requestingDeviceId, probably because there were no one-time keys.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
||||||
//
|
//
|
||||||
// That should eventually resolve itself, but it's poor form.
|
// That should eventually resolve itself, but it's poor form.
|
||||||
|
|
||||||
Timber.v("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
||||||
|
|
||||||
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
||||||
val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams)
|
val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams)
|
||||||
|
@ -90,7 +90,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
||||||
oneTimeKey = key
|
oneTimeKey = key
|
||||||
}
|
}
|
||||||
if (oneTimeKey == null) {
|
if (oneTimeKey == null) {
|
||||||
Timber.v("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
||||||
+ " for device " + userId + " : " + deviceId)
|
+ " for device " + userId + " : " + deviceId)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,8 +243,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||||
Timber.v("## CRYPTO | onRoomKeyEvent(), forward adding key : roomId ${roomKeyContent.roomId}" +
|
Timber.i("## CRYPTO | onRoomKeyEvent(), forward adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||||
" sessionId ${roomKeyContent.sessionId} sessionKey ${roomKeyContent.sessionKey}")
|
|
||||||
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()
|
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
|
@ -273,9 +272,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
|
|
||||||
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key
|
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## CRYPTO | onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
|
Timber.i("## CRYPTO | onRoomKeyEvent(), Adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||||
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event);
|
|
||||||
|
|
||||||
if (null == senderKey) {
|
if (null == senderKey) {
|
||||||
Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
|
Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
|
||||||
return
|
return
|
||||||
|
@ -285,7 +282,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
keysClaimed = event.getKeysClaimed().toMutableMap()
|
keysClaimed = event.getKeysClaimed().toMutableMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.e("## CRYPTO | onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}")
|
Timber.i("## CRYPTO | onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}")
|
||||||
val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId,
|
val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId,
|
||||||
roomKeyContent.sessionKey,
|
roomKeyContent.sessionKey,
|
||||||
roomKeyContent.roomId,
|
roomKeyContent.roomId,
|
||||||
|
@ -349,10 +346,10 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
if (olmSessionResult?.sessionId == null) {
|
if (olmSessionResult?.sessionId == null) {
|
||||||
// no session with this device, probably because there
|
// no session with this device, probably because there
|
||||||
// were no one-time keys.
|
// were no one-time keys.
|
||||||
|
Timber.e("no session with this device $deviceId, probably because there were no one-time keys.")
|
||||||
return@mapCatching
|
return@mapCatching
|
||||||
}
|
}
|
||||||
Timber.v("## CRYPTO | shareKeysWithDevice() : sharing keys for session" +
|
Timber.i("## CRYPTO | shareKeysWithDevice() : sharing session ${body.sessionId} with device $userId:$deviceId")
|
||||||
" ${body.senderKey}|${body.sessionId} with device $userId:$deviceId")
|
|
||||||
|
|
||||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
||||||
runCatching { olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) }
|
runCatching { olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) }
|
||||||
|
@ -363,6 +360,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body")
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -370,9 +368,13 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||||
Timber.v("## CRYPTO | shareKeysWithDevice() : sending to $userId:$deviceId")
|
Timber.i("## CRYPTO | shareKeysWithDevice() : sending ${body.sessionId} to $userId:$deviceId")
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||||
|
try {
|
||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.e(failure, "## CRYPTO | shareKeysWithDevice() : Failed to send ${body.sessionId} to $userId:$deviceId")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,8 +217,10 @@ internal class MXMegolmEncryption(
|
||||||
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
|
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
|
||||||
|
|
||||||
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
Timber.v("## CRYPTO | shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
Timber.v(
|
||||||
+ (System.currentTimeMillis() - t0) + " ms")
|
"""## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
||||||
|
.trimMargin()
|
||||||
|
)
|
||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
var haveTargets = false
|
var haveTargets = false
|
||||||
val userIds = results.userIds
|
val userIds = results.userIds
|
||||||
|
@ -242,7 +244,7 @@ internal class MXMegolmEncryption(
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Timber.v("## CRYPTO | shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
|
Timber.i("## CRYPTO | shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
|
||||||
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo)))
|
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo)))
|
||||||
haveTargets = true
|
haveTargets = true
|
||||||
}
|
}
|
||||||
|
@ -270,21 +272,22 @@ internal class MXMegolmEncryption(
|
||||||
|
|
||||||
if (haveTargets) {
|
if (haveTargets) {
|
||||||
t0 = System.currentTimeMillis()
|
t0 = System.currentTimeMillis()
|
||||||
Timber.v("## CRYPTO | shareUserDevicesKey() : has target")
|
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||||
try {
|
try {
|
||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
Timber.v("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
// What to do here...
|
// What to do here...
|
||||||
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
|
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
|
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notifyKeyWithHeld(targets: List<UserDevice>, sessionId: String, senderKey: String?, code: WithHeldCode) {
|
private fun notifyKeyWithHeld(targets: List<UserDevice>, sessionId: String, senderKey: String?, code: WithHeldCode) {
|
||||||
|
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId ")
|
||||||
val withHeldContent = RoomKeyWithHeldContent(
|
val withHeldContent = RoomKeyWithHeldContent(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
senderKey = senderKey,
|
senderKey = senderKey,
|
||||||
|
@ -393,16 +396,16 @@ internal class MXMegolmEncryption(
|
||||||
userId: String,
|
userId: String,
|
||||||
deviceId: String,
|
deviceId: String,
|
||||||
senderKey: String): Boolean {
|
senderKey: String): Boolean {
|
||||||
Timber.d("[MXMegolmEncryption] reshareKey: $sessionId to $userId:$deviceId")
|
Timber.i("## Crypto process reshareKey for $sessionId to $userId:$deviceId")
|
||||||
val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) ?: return false
|
val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) ?: return false
|
||||||
.also { Timber.w("Device not found") }
|
.also { Timber.w("## Crypto reshareKey: Device not found") }
|
||||||
|
|
||||||
// Get the chain index of the key we previously sent this device
|
// Get the chain index of the key we previously sent this device
|
||||||
val chainIndex = outboundSession?.sharedWithHelper?.wasSharedWith(userId, deviceId) ?: return false
|
val chainIndex = outboundSession?.sharedWithHelper?.wasSharedWith(userId, deviceId) ?: return false
|
||||||
.also {
|
.also {
|
||||||
// Send a room key with held
|
// Send a room key with held
|
||||||
notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED)
|
notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED)
|
||||||
Timber.w("[MXMegolmEncryption] reshareKey : ERROR : Never share megolm with this device")
|
Timber.w("## Crypto reshareKey: ERROR : Never share megolm with this device")
|
||||||
}
|
}
|
||||||
|
|
||||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||||
|
@ -411,9 +414,11 @@ internal class MXMegolmEncryption(
|
||||||
olmSessionResult?.sessionId
|
olmSessionResult?.sessionId
|
||||||
?: // no session with this device, probably because there were no one-time keys.
|
?: // no session with this device, probably because there were no one-time keys.
|
||||||
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
|
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
|
||||||
return false
|
return false.also {
|
||||||
|
Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys")
|
||||||
|
}
|
||||||
|
|
||||||
Timber.d("[MXMegolmEncryption] reshareKey: sharing keys for session $senderKey|$sessionId:$chainIndex with device $userId:$deviceId")
|
Timber.i("[MXMegolmEncryption] reshareKey: sharing keys for session $senderKey|$sessionId:$chainIndex with device $userId:$deviceId")
|
||||||
|
|
||||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
||||||
|
|
||||||
|
@ -425,6 +430,7 @@ internal class MXMegolmEncryption(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
Timber.e(it, "[MXMegolmEncryption] reshareKey: failed to get session $sessionId|$senderKey|$roomId")
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -432,13 +438,14 @@ internal class MXMegolmEncryption(
|
||||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||||
Timber.v("## CRYPTO | CRYPTO | reshareKey() : sending to $userId:$deviceId")
|
Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId")
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||||
return try {
|
return try {
|
||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
|
Timber.i("## CRYPTO reshareKey() : successfully send <$sessionId> to $userId:$deviceId")
|
||||||
true
|
true
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.e(failure, "## CRYPTO | CRYPTO | reshareKey() : fail to send <$sessionId> to $userId:$deviceId")
|
Timber.e(failure, "## CRYPTO reshareKey() : fail to send <$sessionId> to $userId:$deviceId")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ import org.matrix.android.sdk.internal.task.TaskThread
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.internal.util.withoutPrefix
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||||
|
@ -444,7 +443,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
// Maybe it's signed by a locally trusted device?
|
// Maybe it's signed by a locally trusted device?
|
||||||
myMasterKey.signatures?.get(userId)?.forEach { (key, value) ->
|
myMasterKey.signatures?.get(userId)?.forEach { (key, value) ->
|
||||||
val potentialDeviceId = key.withoutPrefix("ed25519:")
|
val potentialDeviceId = key.removePrefix("ed25519:")
|
||||||
val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId)
|
val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId)
|
||||||
if (potentialDevice != null && potentialDevice.isVerified) {
|
if (potentialDevice != null && potentialDevice.isVerified) {
|
||||||
// Check signature validity?
|
// Check signature validity?
|
||||||
|
|
|
@ -241,9 +241,9 @@ internal class UpdateTrustWorker(context: Context,
|
||||||
private fun computeRoomShield(activeMemberUserIds: List<String>, roomSummaryEntity: RoomSummaryEntity): RoomEncryptionTrustLevel {
|
private fun computeRoomShield(activeMemberUserIds: List<String>, roomSummaryEntity: RoomSummaryEntity): RoomEncryptionTrustLevel {
|
||||||
Timber.d("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} -> $activeMemberUserIds")
|
Timber.d("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} -> $activeMemberUserIds")
|
||||||
// The set of “all users” depends on the type of room:
|
// The set of “all users” depends on the type of room:
|
||||||
// For regular / topic rooms, all users including yourself, are considered when decorating a room
|
// For regular / topic rooms which have more than 2 members (including yourself) are considered when decorating a room
|
||||||
// For 1:1 and group DM rooms, all other users (i.e. excluding yourself) are considered when decorating a room
|
// For 1:1 and group DM rooms, all other users (i.e. excluding yourself) are considered when decorating a room
|
||||||
val listToCheck = if (roomSummaryEntity.isDirect) {
|
val listToCheck = if (roomSummaryEntity.isDirect || activeMemberUserIds.size <= 2) {
|
||||||
activeMemberUserIds.filter { it != myUserId }
|
activeMemberUserIds.filter { it != myUserId }
|
||||||
} else {
|
} else {
|
||||||
activeMemberUserIds
|
activeMemberUserIds
|
||||||
|
|
|
@ -1679,27 +1679,24 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
// Only keep one week history
|
// Only keep one week history
|
||||||
realm.where<IncomingGossipingRequestEntity>()
|
realm.where<IncomingGossipingRequestEntity>()
|
||||||
.lessThan(IncomingGossipingRequestEntityFields.LOCAL_CREATION_TIMESTAMP, prevWeekTs)
|
.lessThan(IncomingGossipingRequestEntityFields.LOCAL_CREATION_TIMESTAMP, prevWeekTs)
|
||||||
.findAll().let {
|
.findAll()
|
||||||
Timber.i("## Crypto Clean up ${it.size} IncomingGossipingRequestEntity")
|
.also { Timber.i("## Crypto Clean up ${it.size} IncomingGossipingRequestEntity") }
|
||||||
it.deleteAllFromRealm()
|
.deleteAllFromRealm()
|
||||||
}
|
|
||||||
|
|
||||||
// Clean the cancelled ones?
|
// Clean the cancelled ones?
|
||||||
realm.where<OutgoingGossipingRequestEntity>()
|
realm.where<OutgoingGossipingRequestEntity>()
|
||||||
.equalTo(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, OutgoingGossipingRequestState.CANCELLED.name)
|
.equalTo(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, OutgoingGossipingRequestState.CANCELLED.name)
|
||||||
.equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
|
.equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
|
||||||
.findAll().let {
|
.findAll()
|
||||||
Timber.i("## Crypto Clean up ${it.size} OutgoingGossipingRequestEntity")
|
.also { Timber.i("## Crypto Clean up ${it.size} OutgoingGossipingRequestEntity") }
|
||||||
it.deleteAllFromRealm()
|
.deleteAllFromRealm()
|
||||||
}
|
|
||||||
|
|
||||||
// Only keep one week history
|
// Only keep one week history
|
||||||
realm.where<GossipingEventEntity>()
|
realm.where<GossipingEventEntity>()
|
||||||
.lessThan(GossipingEventEntityFields.AGE_LOCAL_TS, prevWeekTs)
|
.lessThan(GossipingEventEntityFields.AGE_LOCAL_TS, prevWeekTs)
|
||||||
.findAll().let {
|
.findAll()
|
||||||
Timber.i("## Crypto Clean up ${it.size} GossipingEventEntityFields")
|
.also { Timber.i("## Crypto Clean up ${it.size} GossipingEventEntityFields") }
|
||||||
it.deleteAllFromRealm()
|
.deleteAllFromRealm()
|
||||||
}
|
|
||||||
|
|
||||||
// Can we do something for WithHeldSessionEntity?
|
// Can we do something for WithHeldSessionEntity?
|
||||||
}
|
}
|
||||||
|
|
|
@ -537,11 +537,10 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
// If there is a corresponding request, we can auto accept
|
// If there is a corresponding request, we can auto accept
|
||||||
// as we are the one requesting in first place (or we accepted the request)
|
// as we are the one requesting in first place (or we accepted the request)
|
||||||
// I need to check if the pending request was related to this device also
|
// I need to check if the pending request was related to this device also
|
||||||
val autoAccept = getExistingVerificationRequest(otherUserId)?.any {
|
val autoAccept = getExistingVerificationRequests(otherUserId).any {
|
||||||
it.transactionId == startReq.transactionId
|
it.transactionId == startReq.transactionId
|
||||||
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
||||||
}
|
}
|
||||||
?: false
|
|
||||||
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
// this,
|
// this,
|
||||||
setDeviceVerificationAction,
|
setDeviceVerificationAction,
|
||||||
|
@ -837,8 +836,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
// SAS do not care for now?
|
// SAS do not care for now?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now transactions are udated, let's also update Requests
|
// Now transactions are updated, let's also update Requests
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId }
|
val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == doneReq.transactionId }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
||||||
return
|
return
|
||||||
|
@ -892,7 +891,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
private fun handleReadyReceived(senderId: String,
|
private fun handleReadyReceived(senderId: String,
|
||||||
readyReq: ValidVerificationInfoReady,
|
readyReq: ValidVerificationInfoReady,
|
||||||
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionId }
|
val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == readyReq.transactionId }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}")
|
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}")
|
||||||
return
|
return
|
||||||
|
@ -1041,9 +1040,9 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>? {
|
override fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest> {
|
||||||
synchronized(lock = pendingRequests) {
|
synchronized(lock = pendingRequests) {
|
||||||
return pendingRequests[otherUserId]
|
return pendingRequests[otherUserId].orEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1205,7 +1204,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
Timber.i("## Requesting verification to user: $otherUserId with device list $otherDevices")
|
Timber.i("## Requesting verification to user: $otherUserId with device list $otherDevices")
|
||||||
|
|
||||||
val targetDevices = otherDevices ?: cryptoStore.getUserDevices(otherUserId)
|
val targetDevices = otherDevices ?: cryptoStore.getUserDevices(otherUserId)
|
||||||
?.values?.map { it.deviceId } ?: emptyList()
|
?.values?.map { it.deviceId }.orEmpty()
|
||||||
|
|
||||||
val requestsForUser = pendingRequests.getOrPut(otherUserId) { mutableListOf() }
|
val requestsForUser = pendingRequests.getOrPut(otherUserId) { mutableListOf() }
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.extensions.toUnsignedInt
|
import org.matrix.android.sdk.internal.extensions.toUnsignedInt
|
||||||
import org.matrix.android.sdk.internal.util.withoutPrefix
|
|
||||||
import org.matrix.olm.OlmSAS
|
import org.matrix.olm.OlmSAS
|
||||||
import org.matrix.olm.OlmUtility
|
import org.matrix.olm.OlmUtility
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -250,7 +249,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||||
|
|
||||||
// cannot be empty because it has been validated
|
// cannot be empty because it has been validated
|
||||||
theirMacSafe.mac.keys.forEach {
|
theirMacSafe.mac.keys.forEach {
|
||||||
val keyIDNoPrefix = it.withoutPrefix("ed25519:")
|
val keyIDNoPrefix = it.removePrefix("ed25519:")
|
||||||
val otherDeviceKey = otherUserKnownDevices?.get(keyIDNoPrefix)?.fingerprint()
|
val otherDeviceKey = otherUserKnownDevices?.get(keyIDNoPrefix)?.fingerprint()
|
||||||
if (otherDeviceKey == null) {
|
if (otherDeviceKey == null) {
|
||||||
Timber.w("## SAS Verification: Could not find device $keyIDNoPrefix to verify")
|
Timber.w("## SAS Verification: Could not find device $keyIDNoPrefix to verify")
|
||||||
|
@ -273,7 +272,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||||
if (otherCrossSigningMasterKeyPublic != null) {
|
if (otherCrossSigningMasterKeyPublic != null) {
|
||||||
// Did the user signed his master key
|
// Did the user signed his master key
|
||||||
theirMacSafe.mac.keys.forEach {
|
theirMacSafe.mac.keys.forEach {
|
||||||
val keyIDNoPrefix = it.withoutPrefix("ed25519:")
|
val keyIDNoPrefix = it.removePrefix("ed25519:")
|
||||||
if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) {
|
if (keyIDNoPrefix == otherCrossSigningMasterKeyPublic) {
|
||||||
// Check the signature
|
// Check the signature
|
||||||
val mac = macUsingAgreedMethod(otherCrossSigningMasterKeyPublic, baseInfo + it)
|
val mac = macUsingAgreedMethod(otherCrossSigningMasterKeyPublic, baseInfo + it)
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import io.realm.Realm
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
import org.matrix.android.sdk.internal.session.securestorage.SecretStoringUtils
|
import org.matrix.android.sdk.internal.session.securestorage.SecretStoringUtils
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
|
@ -46,7 +47,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
|
||||||
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
|
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
private fun generateKeyForRealm(): ByteArray {
|
private fun generateKeyForRealm(): ByteArray {
|
||||||
val keyForRealm = ByteArray(RealmConfiguration.KEY_LENGTH)
|
val keyForRealm = ByteArray(Realm.ENCRYPTION_KEY_LENGTH)
|
||||||
rng.nextBytes(keyForRealm)
|
rng.nextBytes(keyForRealm)
|
||||||
return keyForRealm
|
return keyForRealm
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
|
||||||
.apply {
|
.apply {
|
||||||
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
||||||
}
|
}
|
||||||
|
.allowWritesOnUiThread(true)
|
||||||
.modules(SessionRealmModule())
|
.modules(SessionRealmModule())
|
||||||
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
|
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
|
||||||
.migration(migration)
|
.migration(migration)
|
||||||
|
|
|
@ -52,5 +52,8 @@ internal class TimeOutInterceptor @Inject constructor() : Interceptor {
|
||||||
const val CONNECT_TIMEOUT = "CONNECT_TIMEOUT"
|
const val CONNECT_TIMEOUT = "CONNECT_TIMEOUT"
|
||||||
const val READ_TIMEOUT = "READ_TIMEOUT"
|
const val READ_TIMEOUT = "READ_TIMEOUT"
|
||||||
const val WRITE_TIMEOUT = "WRITE_TIMEOUT"
|
const val WRITE_TIMEOUT = "WRITE_TIMEOUT"
|
||||||
|
|
||||||
|
// 1 minute
|
||||||
|
const val DEFAULT_LONG_TIMEOUT: Long = 60_000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,45 +16,28 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.raw
|
package org.matrix.android.sdk.internal.raw
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.raw.RawCacheStrategy
|
import org.matrix.android.sdk.api.raw.RawCacheStrategy
|
||||||
import org.matrix.android.sdk.api.raw.RawService
|
import org.matrix.android.sdk.api.raw.RawService
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultRawService @Inject constructor(
|
internal class DefaultRawService @Inject constructor(
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
private val getUrlTask: GetUrlTask,
|
private val getUrlTask: GetUrlTask,
|
||||||
private val cleanRawCacheTask: CleanRawCacheTask
|
private val cleanRawCacheTask: CleanRawCacheTask
|
||||||
) : RawService {
|
) : RawService {
|
||||||
override fun getUrl(url: String,
|
override suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String {
|
||||||
rawCacheStrategy: RawCacheStrategy,
|
return getUrlTask.execute(GetUrlTask.Params(url, rawCacheStrategy))
|
||||||
matrixCallback: MatrixCallback<String>): Cancelable {
|
|
||||||
return getUrlTask
|
|
||||||
.configureWith(GetUrlTask.Params(url, rawCacheStrategy)) {
|
|
||||||
callback = matrixCallback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getWellknown(userId: String,
|
override suspend fun getWellknown(userId: String): String {
|
||||||
matrixCallback: MatrixCallback<String>): Cancelable {
|
|
||||||
val homeServerDomain = userId.substringAfter(":")
|
val homeServerDomain = userId.substringAfter(":")
|
||||||
return getUrl(
|
return getUrl(
|
||||||
"https://$homeServerDomain/.well-known/matrix/client",
|
"https://$homeServerDomain/.well-known/matrix/client",
|
||||||
RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false),
|
RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false)
|
||||||
matrixCallback
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun clearCache() {
|
||||||
return cleanRawCacheTask
|
cleanRawCacheTask.execute(Unit)
|
||||||
.configureWith(Unit) {
|
|
||||||
callback = matrixCallback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,30 +16,17 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.account
|
package org.matrix.android.sdk.internal.session.account
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.account.AccountService
|
import org.matrix.android.sdk.api.session.account.AccountService
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask,
|
internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask,
|
||||||
private val deactivateAccountTask: DeactivateAccountTask,
|
private val deactivateAccountTask: DeactivateAccountTask) : AccountService {
|
||||||
private val taskExecutor: TaskExecutor) : AccountService {
|
|
||||||
|
|
||||||
override fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun changePassword(password: String, newPassword: String) {
|
||||||
return changePasswordTask
|
changePasswordTask.execute(ChangePasswordTask.Params(password, newPassword))
|
||||||
.configureWith(ChangePasswordTask.Params(password, newPassword)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deactivateAccount(password: String, eraseAllData: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun deactivateAccount(password: String, eraseAllData: Boolean) {
|
||||||
return deactivateAccountTask
|
deactivateAccountTask.execute(DeactivateAccountTask.Params(password, eraseAllData))
|
||||||
.configureWith(DeactivateAccountTask.Params(password, eraseAllData)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,13 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.group
|
package org.matrix.android.sdk.internal.session.group
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.group.Group
|
import org.matrix.android.sdk.api.session.group.Group
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
|
|
||||||
internal class DefaultGroup(override val groupId: String,
|
internal class DefaultGroup(override val groupId: String,
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
private val getGroupDataTask: GetGroupDataTask) : Group {
|
private val getGroupDataTask: GetGroupDataTask) : Group {
|
||||||
|
|
||||||
override fun fetchGroupData(callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun fetchGroupData() {
|
||||||
val params = GetGroupDataTask.Params.FetchWithIds(listOf(groupId))
|
val params = GetGroupDataTask.Params.FetchWithIds(listOf(groupId))
|
||||||
return getGroupDataTask.configureWith(params) {
|
getGroupDataTask.execute(params)
|
||||||
this.callback = callback
|
|
||||||
}.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.group
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.group.Group
|
import org.matrix.android.sdk.api.session.group.Group
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface GroupFactory {
|
internal interface GroupFactory {
|
||||||
|
@ -26,14 +25,12 @@ internal interface GroupFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask,
|
internal class DefaultGroupFactory @Inject constructor(private val getGroupDataTask: GetGroupDataTask) :
|
||||||
private val taskExecutor: TaskExecutor) :
|
|
||||||
GroupFactory {
|
GroupFactory {
|
||||||
|
|
||||||
override fun create(groupId: String): Group {
|
override fun create(groupId: String): Group {
|
||||||
return DefaultGroup(
|
return DefaultGroup(
|
||||||
groupId = groupId,
|
groupId = groupId,
|
||||||
taskExecutor = taskExecutor,
|
|
||||||
getGroupDataTask = getGroupDataTask
|
getGroupDataTask = getGroupDataTask
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.internal.util.ensureProtocol
|
import org.matrix.android.sdk.internal.util.ensureProtocol
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
@ -243,7 +244,20 @@ internal class DefaultIdentityService @Inject constructor(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUserConsent(): Boolean {
|
||||||
|
return identityStore.getIdentityData()?.userConsent.orFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUserConsent(newValue: Boolean) {
|
||||||
|
identityStore.setUserConsent(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
override fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable {
|
override fun lookUp(threePids: List<ThreePid>, callback: MatrixCallback<List<FoundThreePid>>): Cancelable {
|
||||||
|
if (!getUserConsent()) {
|
||||||
|
callback.onFailure(IdentityServiceError.UserConsentNotProvided)
|
||||||
|
return NoOpCancellable
|
||||||
|
}
|
||||||
|
|
||||||
if (threePids.isEmpty()) {
|
if (threePids.isEmpty()) {
|
||||||
callback.onSuccess(emptyList())
|
callback.onSuccess(emptyList())
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
|
@ -255,6 +269,9 @@ internal class DefaultIdentityService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getShareStatus(threePids: List<ThreePid>, callback: MatrixCallback<Map<ThreePid, SharedState>>): Cancelable {
|
override fun getShareStatus(threePids: List<ThreePid>, callback: MatrixCallback<Map<ThreePid, SharedState>>): Cancelable {
|
||||||
|
// Note: we do not require user consent here, because it is used for emails and phone numbers that the user has already sent
|
||||||
|
// to the home server, and not emails and phone numbers from the contact book of the user
|
||||||
|
|
||||||
if (threePids.isEmpty()) {
|
if (threePids.isEmpty()) {
|
||||||
callback.onSuccess(emptyMap())
|
callback.onSuccess(emptyMap())
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.session.identity.db.IdentityRealmModule
|
||||||
import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStore
|
import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStore
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStoreMigration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@ -59,6 +60,7 @@ internal abstract class IdentityModule {
|
||||||
@SessionScope
|
@SessionScope
|
||||||
fun providesIdentityRealmConfiguration(realmKeysUtils: RealmKeysUtils,
|
fun providesIdentityRealmConfiguration(realmKeysUtils: RealmKeysUtils,
|
||||||
@SessionFilesDirectory directory: File,
|
@SessionFilesDirectory directory: File,
|
||||||
|
migration: RealmIdentityStoreMigration,
|
||||||
@UserMd5 userMd5: String): RealmConfiguration {
|
@UserMd5 userMd5: String): RealmConfiguration {
|
||||||
return RealmConfiguration.Builder()
|
return RealmConfiguration.Builder()
|
||||||
.directory(directory)
|
.directory(directory)
|
||||||
|
@ -66,6 +68,9 @@ internal abstract class IdentityModule {
|
||||||
.apply {
|
.apply {
|
||||||
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
||||||
}
|
}
|
||||||
|
.schemaVersion(RealmIdentityStoreMigration.IDENTITY_STORE_SCHEMA_VERSION)
|
||||||
|
.migration(migration)
|
||||||
|
.allowWritesOnUiThread(true)
|
||||||
.modules(IdentityRealmModule())
|
.modules(IdentityRealmModule())
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,6 @@ internal data class IdentityData(
|
||||||
val identityServerUrl: String?,
|
val identityServerUrl: String?,
|
||||||
val token: String?,
|
val token: String?,
|
||||||
val hashLookupPepper: String?,
|
val hashLookupPepper: String?,
|
||||||
val hashLookupAlgorithm: List<String>
|
val hashLookupAlgorithm: List<String>,
|
||||||
|
val userConsent: Boolean
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,6 +27,8 @@ internal interface IdentityStore {
|
||||||
|
|
||||||
fun setToken(token: String?)
|
fun setToken(token: String?)
|
||||||
|
|
||||||
|
fun setUserConsent(consent: Boolean)
|
||||||
|
|
||||||
fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse)
|
fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,7 +23,8 @@ internal open class IdentityDataEntity(
|
||||||
var identityServerUrl: String? = null,
|
var identityServerUrl: String? = null,
|
||||||
var token: String? = null,
|
var token: String? = null,
|
||||||
var hashLookupPepper: String? = null,
|
var hashLookupPepper: String? = null,
|
||||||
var hashLookupAlgorithm: RealmList<String> = RealmList()
|
var hashLookupAlgorithm: RealmList<String> = RealmList(),
|
||||||
|
var userConsent: Boolean = false
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
|
|
|
@ -52,6 +52,13 @@ internal fun IdentityDataEntity.Companion.setToken(realm: Realm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun IdentityDataEntity.Companion.setUserConsent(realm: Realm,
|
||||||
|
newConsent: Boolean) {
|
||||||
|
get(realm)?.apply {
|
||||||
|
userConsent = newConsent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun IdentityDataEntity.Companion.setHashDetails(realm: Realm,
|
internal fun IdentityDataEntity.Companion.setHashDetails(realm: Realm,
|
||||||
pepper: String,
|
pepper: String,
|
||||||
algorithms: List<String>) {
|
algorithms: List<String>) {
|
||||||
|
|
|
@ -26,7 +26,8 @@ internal object IdentityMapper {
|
||||||
identityServerUrl = entity.identityServerUrl,
|
identityServerUrl = entity.identityServerUrl,
|
||||||
token = entity.token,
|
token = entity.token,
|
||||||
hashLookupPepper = entity.hashLookupPepper,
|
hashLookupPepper = entity.hashLookupPepper,
|
||||||
hashLookupAlgorithm = entity.hashLookupAlgorithm.toList()
|
hashLookupAlgorithm = entity.hashLookupAlgorithm.toList(),
|
||||||
|
userConsent = entity.userConsent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,14 @@ internal class RealmIdentityStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setUserConsent(consent: Boolean) {
|
||||||
|
Realm.getInstance(realmConfiguration).use {
|
||||||
|
it.executeTransaction { realm ->
|
||||||
|
IdentityDataEntity.setUserConsent(realm, consent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse) {
|
override fun setHashDetails(hashDetailResponse: IdentityHashDetailResponse) {
|
||||||
Realm.getInstance(realmConfiguration).use {
|
Realm.getInstance(realmConfiguration).use {
|
||||||
it.executeTransaction { realm ->
|
it.executeTransaction { realm ->
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.identity.db
|
||||||
|
|
||||||
|
import io.realm.DynamicRealm
|
||||||
|
import io.realm.RealmMigration
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class RealmIdentityStoreMigration @Inject constructor() : RealmMigration {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val IDENTITY_STORE_SCHEMA_VERSION = 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||||
|
Timber.v("Migrating Realm Identity from $oldVersion to $newVersion")
|
||||||
|
|
||||||
|
if (oldVersion <= 0) migrateTo1(realm)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateTo1(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 0 -> 1")
|
||||||
|
Timber.d("Add field userConsent (Boolean) and set the value to false")
|
||||||
|
|
||||||
|
realm.schema.get("IdentityDataEntity")
|
||||||
|
?.addField(IdentityDataEntityFields.USER_CONSENT, Boolean::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,6 @@
|
||||||
package org.matrix.android.sdk.internal.session.notification
|
package org.matrix.android.sdk.internal.session.notification
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.pushrules.PushRuleService
|
import org.matrix.android.sdk.api.pushrules.PushRuleService
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleKind
|
import org.matrix.android.sdk.api.pushrules.RuleKind
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleSetKey
|
import org.matrix.android.sdk.api.pushrules.RuleSetKey
|
||||||
|
@ -24,7 +23,6 @@ import org.matrix.android.sdk.api.pushrules.getActions
|
||||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||||
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
|
import org.matrix.android.sdk.api.pushrules.rest.RuleSet
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
|
import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
|
||||||
import org.matrix.android.sdk.internal.database.model.PushRulesEntity
|
import org.matrix.android.sdk.internal.database.model.PushRulesEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
|
@ -103,37 +101,21 @@ internal class DefaultPushRuleService @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun updatePushRuleEnableStatus(kind: RuleKind, pushRule: PushRule, enabled: Boolean) {
|
||||||
// The rules will be updated, and will come back from the next sync response
|
// The rules will be updated, and will come back from the next sync response
|
||||||
return updatePushRuleEnableStatusTask
|
updatePushRuleEnableStatusTask.execute(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled))
|
||||||
.configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addPushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun addPushRule(kind: RuleKind, pushRule: PushRule) {
|
||||||
return addPushRuleTask
|
addPushRuleTask.execute(AddPushRuleTask.Params(kind, pushRule))
|
||||||
.configureWith(AddPushRuleTask.Params(kind, pushRule)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun updatePushRuleActions(kind: RuleKind, oldPushRule: PushRule, newPushRule: PushRule) {
|
||||||
return updatePushRuleActionsTask
|
updatePushRuleActionsTask.execute(UpdatePushRuleActionsTask.Params(kind, oldPushRule, newPushRule))
|
||||||
.configureWith(UpdatePushRuleActionsTask.Params(kind, oldPushRule, newPushRule)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removePushRule(kind: RuleKind, pushRule: PushRule, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun removePushRule(kind: RuleKind, pushRule: PushRule) {
|
||||||
return removePushRuleTask
|
removePushRuleTask.execute(RemovePushRuleTask.Params(kind, pushRule))
|
||||||
.configureWith(RemovePushRuleTask.Params(kind, pushRule)) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
|
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.UserThreePidEntity
|
import org.matrix.android.sdk.internal.database.model.UserThreePidEntity
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.session.content.FileUploader
|
import org.matrix.android.sdk.internal.session.content.FileUploader
|
||||||
|
import org.matrix.android.sdk.internal.session.user.UserStore
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||||
|
@ -49,6 +50,7 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
|
||||||
private val finalizeAddingThreePidTask: FinalizeAddingThreePidTask,
|
private val finalizeAddingThreePidTask: FinalizeAddingThreePidTask,
|
||||||
private val deleteThreePidTask: DeleteThreePidTask,
|
private val deleteThreePidTask: DeleteThreePidTask,
|
||||||
private val pendingThreePidMapper: PendingThreePidMapper,
|
private val pendingThreePidMapper: PendingThreePidMapper,
|
||||||
|
private val userStore: UserStore,
|
||||||
private val fileUploader: FileUploader) : ProfileService {
|
private val fileUploader: FileUploader) : ProfileService {
|
||||||
|
|
||||||
override fun getDisplayName(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable {
|
override fun getDisplayName(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable {
|
||||||
|
@ -70,17 +72,17 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDisplayName(userId: String, newDisplayName: String, matrixCallback: MatrixCallback<Unit>): Cancelable {
|
override fun setDisplayName(userId: String, newDisplayName: String, matrixCallback: MatrixCallback<Unit>): Cancelable {
|
||||||
return setDisplayNameTask
|
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.io, matrixCallback) {
|
||||||
.configureWith(SetDisplayNameTask.Params(userId = userId, newDisplayName = newDisplayName)) {
|
setDisplayNameTask.execute(SetDisplayNameTask.Params(userId = userId, newDisplayName = newDisplayName))
|
||||||
callback = matrixCallback
|
userStore.updateDisplayName(userId, newDisplayName)
|
||||||
}
|
}
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String, matrixCallback: MatrixCallback<Unit>): Cancelable {
|
override fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String, matrixCallback: MatrixCallback<Unit>): Cancelable {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, matrixCallback) {
|
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, matrixCallback) {
|
||||||
val response = fileUploader.uploadFromUri(newAvatarUri, fileName, "image/jpeg")
|
val response = fileUploader.uploadFromUri(newAvatarUri, fileName, "image/jpeg")
|
||||||
setAvatarUrlTask.execute(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri))
|
setAvatarUrlTask.execute(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri))
|
||||||
|
userStore.updateAvatar(userId, response.contentUri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,13 +101,13 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
||||||
return cryptoService.shouldEncryptForInvitedMembers(roomId)
|
return cryptoService.shouldEncryptForInvitedMembers(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun enableEncryption(algorithm: String, callback: MatrixCallback<Unit>) {
|
override suspend fun enableEncryption(algorithm: String) {
|
||||||
when {
|
when {
|
||||||
isEncrypted() -> {
|
isEncrypted() -> {
|
||||||
callback.onFailure(IllegalStateException("Encryption is already enabled for this room"))
|
throw IllegalStateException("Encryption is already enabled for this room")
|
||||||
}
|
}
|
||||||
algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> {
|
algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> {
|
||||||
callback.onFailure(InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported"))
|
throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val params = SendStateTask.Params(
|
val params = SendStateTask.Params(
|
||||||
|
@ -118,11 +118,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
||||||
"algorithm" to algorithm
|
"algorithm" to algorithm
|
||||||
))
|
))
|
||||||
|
|
||||||
sendStateTask
|
sendStateTask.execute(params)
|
||||||
.configureWith(params) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,27 +17,37 @@
|
||||||
package org.matrix.android.sdk.internal.session.room
|
package org.matrix.android.sdk.internal.session.room
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Transformations
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.api.session.room.RoomService
|
import org.matrix.android.sdk.api.session.room.RoomService
|
||||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
|
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.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
||||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
|
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask
|
import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask
|
||||||
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
|
import org.matrix.android.sdk.internal.util.fetchCopied
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultRoomService @Inject constructor(
|
internal class DefaultRoomService @Inject constructor(
|
||||||
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val createRoomTask: CreateRoomTask,
|
private val createRoomTask: CreateRoomTask,
|
||||||
private val joinRoomTask: JoinRoomTask,
|
private val joinRoomTask: JoinRoomTask,
|
||||||
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
|
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
|
||||||
|
@ -118,4 +128,24 @@ internal class DefaultRoomService @Inject constructor(
|
||||||
override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> {
|
override fun getChangeMembershipsLive(): LiveData<Map<String, ChangeMembershipState>> {
|
||||||
return roomChangeMembershipStateDataSource.getLiveStates()
|
return roomChangeMembershipStateDataSource.getLiveStates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getRoomMember(userId: String, roomId: String): RoomMemberSummary? {
|
||||||
|
val roomMemberEntity = monarchy.fetchCopied {
|
||||||
|
RoomMemberHelper(it, roomId).getLastRoomMember(userId)
|
||||||
|
}
|
||||||
|
return roomMemberEntity?.asDomain()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRoomMemberLive(userId: String, roomId: String): LiveData<Optional<RoomMemberSummary>> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm ->
|
||||||
|
RoomMemberHelper(realm, roomId).queryRoomMembersEvent()
|
||||||
|
.equalTo(RoomMemberSummaryEntityFields.USER_ID, userId)
|
||||||
|
},
|
||||||
|
{ it.asDomain() }
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) { results ->
|
||||||
|
results.firstOrNull().toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
|
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomAvatarResolver @Inject constructor(@UserId private val userId: String) {
|
internal class RoomAvatarResolver @Inject constructor(@UserId private val userId: String) {
|
||||||
|
@ -46,12 +48,15 @@ internal class RoomAvatarResolver @Inject constructor(@UserId private val userId
|
||||||
val roomMembers = RoomMemberHelper(realm, roomId)
|
val roomMembers = RoomMemberHelper(realm, roomId)
|
||||||
val members = roomMembers.queryActiveRoomMembersEvent().findAll()
|
val members = roomMembers.queryActiveRoomMembersEvent().findAll()
|
||||||
// detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat)
|
// detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat)
|
||||||
|
val isDirectRoom = RoomSummaryEntity.where(realm, roomId).findFirst()?.isDirect ?: false
|
||||||
|
if (isDirectRoom) {
|
||||||
if (members.size == 1) {
|
if (members.size == 1) {
|
||||||
res = members.firstOrNull()?.avatarUrl
|
res = members.firstOrNull()?.avatarUrl
|
||||||
} else if (members.size == 2) {
|
} else if (members.size == 2) {
|
||||||
val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst()
|
val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst()
|
||||||
res = firstOtherMember?.avatarUrl
|
res = firstOtherMember?.avatarUrl
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
package org.matrix.android.sdk.internal.session.room.alias
|
package org.matrix.android.sdk.internal.session.room.alias
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Realm
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.findByAlias
|
import org.matrix.android.sdk.internal.database.query.findByAlias
|
||||||
|
@ -24,8 +27,6 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import io.realm.Realm
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, Optional<String>> {
|
internal interface GetRoomIdByAliasTask : Task<GetRoomIdByAliasTask.Params, Optional<String>> {
|
||||||
|
@ -50,9 +51,11 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor(
|
||||||
} else if (!params.searchOnServer) {
|
} else if (!params.searchOnServer) {
|
||||||
Optional.from<String>(null)
|
Optional.from<String>(null)
|
||||||
} else {
|
} else {
|
||||||
roomId = executeRequest<RoomAliasDescription>(eventBus) {
|
roomId = tryOrNull("## Failed to get roomId from alias") {
|
||||||
|
executeRequest<RoomAliasDescription>(eventBus) {
|
||||||
apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
|
apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
|
||||||
}.roomId
|
}
|
||||||
|
}?.roomId
|
||||||
Optional.from(roomId)
|
Optional.from(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,8 +74,8 @@ internal data class CreateRoomBody(
|
||||||
val invite3pids: List<ThreePidInviteBody>?,
|
val invite3pids: List<ThreePidInviteBody>?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extra keys to be added to the content of the m.room.create.
|
* Extra keys, such as m.federate, to be added to the content of the m.room.create event.
|
||||||
* The server will clobber the following keys: creator.
|
* The server will clobber the following keys: creator, room_version.
|
||||||
* Future versions of the specification may allow the server to clobber other keys.
|
* Future versions of the specification may allow the server to clobber other keys.
|
||||||
*/
|
*/
|
||||||
@Json(name = "creation_content")
|
@Json(name = "creation_content")
|
||||||
|
|
|
@ -81,7 +81,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
||||||
topic = params.topic,
|
topic = params.topic,
|
||||||
invitedUserIds = params.invitedUserIds,
|
invitedUserIds = params.invitedUserIds,
|
||||||
invite3pids = invite3pids,
|
invite3pids = invite3pids,
|
||||||
creationContent = params.creationContent,
|
creationContent = params.creationContent.takeIf { it.isNotEmpty() },
|
||||||
initialStates = initialStates,
|
initialStates = initialStates,
|
||||||
preset = params.preset,
|
preset = params.preset,
|
||||||
isDirect = params.isDirect,
|
isDirect = params.isDirect,
|
||||||
|
|
|
@ -31,8 +31,10 @@ import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
|
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||||
import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
|
import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
|
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||||
|
@ -45,6 +47,7 @@ internal interface CreateRoomTask : Task<CreateRoomParams, String>
|
||||||
|
|
||||||
internal class DefaultCreateRoomTask @Inject constructor(
|
internal class DefaultCreateRoomTask @Inject constructor(
|
||||||
private val roomAPI: RoomAPI,
|
private val roomAPI: RoomAPI,
|
||||||
|
@UserId private val userId: String,
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val directChatsHelper: DirectChatsHelper,
|
private val directChatsHelper: DirectChatsHelper,
|
||||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
|
@ -61,6 +64,31 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
||||||
?: throw IllegalStateException("You can't create a direct room without an invitedUser")
|
?: throw IllegalStateException("You can't create a direct room without an invitedUser")
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
|
if (params.preset == CreateRoomPreset.PRESET_PUBLIC_CHAT) {
|
||||||
|
if (params.roomAliasName.isNullOrEmpty()) {
|
||||||
|
throw CreateRoomFailure.RoomAliasError.AliasEmpty
|
||||||
|
}
|
||||||
|
// Check alias availability
|
||||||
|
val fullAlias = "#" + params.roomAliasName + ":" + userId.substringAfter(":")
|
||||||
|
try {
|
||||||
|
executeRequest<RoomAliasDescription>(eventBus) {
|
||||||
|
apiCall = roomAPI.getRoomIdByAlias(fullAlias)
|
||||||
|
}
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
if (throwable is Failure.ServerError && throwable.httpCode == 404) {
|
||||||
|
// This is a 404, so the alias is available: nominal case
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
// Other error, propagate it
|
||||||
|
throw throwable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?.let {
|
||||||
|
// Alias already exists: error case
|
||||||
|
throw CreateRoomFailure.RoomAliasError.AliasNotAvailable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val createRoomBody = createRoomBodyBuilder.build(params)
|
val createRoomBody = createRoomBodyBuilder.build(params)
|
||||||
|
|
||||||
val createRoomResponse = try {
|
val createRoomResponse = try {
|
||||||
|
@ -68,15 +96,19 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
||||||
apiCall = roomAPI.createRoom(createRoomBody)
|
apiCall = roomAPI.createRoom(createRoomBody)
|
||||||
}
|
}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
if (throwable is Failure.ServerError
|
if (throwable is Failure.ServerError) {
|
||||||
&& throwable.httpCode == 403
|
if (throwable.httpCode == 403
|
||||||
&& throwable.error.code == MatrixError.M_FORBIDDEN
|
&& throwable.error.code == MatrixError.M_FORBIDDEN
|
||||||
&& throwable.error.message.startsWith("Federation denied with")) {
|
&& throwable.error.message.startsWith("Federation denied with")) {
|
||||||
throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error)
|
throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error)
|
||||||
} else {
|
} else if (throwable.httpCode == 400
|
||||||
throw throwable
|
&& throwable.error.code == MatrixError.M_UNKNOWN
|
||||||
|
&& throwable.error.message == "Invalid characters in room alias") {
|
||||||
|
throw CreateRoomFailure.RoomAliasError.AliasInvalid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw throwable
|
||||||
|
}
|
||||||
val roomId = createRoomResponse.roomId
|
val roomId = createRoomResponse.roomId
|
||||||
// Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before)
|
// Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before)
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -19,18 +19,14 @@ package org.matrix.android.sdk.internal.session.room.draft
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.room.send.DraftService
|
import org.matrix.android.sdk.api.session.room.send.DraftService
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
|
|
||||||
internal class DefaultDraftService @AssistedInject constructor(@Assisted private val roomId: String,
|
internal class DefaultDraftService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
private val draftRepository: DraftRepository,
|
private val draftRepository: DraftRepository,
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||||
) : DraftService {
|
) : DraftService {
|
||||||
|
|
||||||
|
@ -43,14 +39,14 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
|
||||||
* The draft stack can contain several drafts. Depending of the draft to save, it will update the top draft, or create a new draft,
|
* The draft stack can contain several drafts. Depending of the draft to save, it will update the top draft, or create a new draft,
|
||||||
* or even move an existing draft to the top of the list
|
* or even move an existing draft to the top of the list
|
||||||
*/
|
*/
|
||||||
override fun saveDraft(draft: UserDraft, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun saveDraft(draft: UserDraft) {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
withContext(coroutineDispatchers.main) {
|
||||||
draftRepository.saveDraft(roomId, draft)
|
draftRepository.saveDraft(roomId, draft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun deleteDraft() {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
withContext(coroutineDispatchers.main) {
|
||||||
draftRepository.deleteDraft(roomId)
|
draftRepository.deleteDraft(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,8 @@ internal class RoomDisplayNameResolver @Inject constructor(
|
||||||
}
|
}
|
||||||
} else if (roomEntity?.membership == Membership.JOIN) {
|
} else if (roomEntity?.membership == Membership.JOIN) {
|
||||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
|
val invitedCount = roomSummary?.invitedMembersCount ?: 0
|
||||||
|
val joinedCount = roomSummary?.joinedMembersCount ?: 0
|
||||||
val otherMembersSubset: List<RoomMemberSummaryEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
val otherMembersSubset: List<RoomMemberSummaryEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
||||||
roomSummary.heroes.mapNotNull { userId ->
|
roomSummary.heroes.mapNotNull { userId ->
|
||||||
roomMembers.getLastRoomMember(userId)?.takeIf {
|
roomMembers.getLastRoomMember(userId)?.takeIf {
|
||||||
|
@ -102,22 +104,49 @@ internal class RoomDisplayNameResolver @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
activeMembers.where()
|
activeMembers.where()
|
||||||
.notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId)
|
.notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId)
|
||||||
.limit(3)
|
.limit(5)
|
||||||
.findAll()
|
.findAll()
|
||||||
.createSnapshot()
|
.createSnapshot()
|
||||||
}
|
}
|
||||||
val otherMembersCount = otherMembersSubset.count()
|
val otherMembersCount = otherMembersSubset.count()
|
||||||
name = when (otherMembersCount) {
|
name = when (otherMembersCount) {
|
||||||
0 -> stringProvider.getString(R.string.room_displayname_empty_room)
|
0 -> {
|
||||||
|
stringProvider.getString(R.string.room_displayname_empty_room)
|
||||||
|
// TODO (was xx and yyy) ...
|
||||||
|
}
|
||||||
1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers)
|
1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers)
|
||||||
2 -> stringProvider.getString(R.string.room_displayname_two_members,
|
2 -> {
|
||||||
|
stringProvider.getString(R.string.room_displayname_two_members,
|
||||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||||
resolveRoomMemberName(otherMembersSubset[1], roomMembers)
|
resolveRoomMemberName(otherMembersSubset[1], roomMembers)
|
||||||
)
|
)
|
||||||
else -> stringProvider.getQuantityString(R.plurals.room_displayname_three_and_more_members,
|
}
|
||||||
roomMembers.getNumberOfJoinedMembers() - 1,
|
3 -> {
|
||||||
|
stringProvider.getString(R.string.room_displayname_3_members,
|
||||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||||
roomMembers.getNumberOfJoinedMembers() - 1)
|
resolveRoomMemberName(otherMembersSubset[1], roomMembers),
|
||||||
|
resolveRoomMemberName(otherMembersSubset[2], roomMembers)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
4 -> {
|
||||||
|
stringProvider.getString(R.string.room_displayname_4_members,
|
||||||
|
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||||
|
resolveRoomMemberName(otherMembersSubset[1], roomMembers),
|
||||||
|
resolveRoomMemberName(otherMembersSubset[2], roomMembers),
|
||||||
|
resolveRoomMemberName(otherMembersSubset[3], roomMembers)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val remainingCount = invitedCount + joinedCount - otherMembersCount + 1
|
||||||
|
stringProvider.getQuantityString(
|
||||||
|
R.plurals.room_displayname_four_and_more_members,
|
||||||
|
remainingCount,
|
||||||
|
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||||
|
resolveRoomMemberName(otherMembersSubset[1], roomMembers),
|
||||||
|
resolveRoomMemberName(otherMembersSubset[2], roomMembers),
|
||||||
|
remainingCount
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return name ?: roomId
|
return name ?: roomId
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.room.membership
|
package org.matrix.android.sdk.internal.session.room.membership
|
||||||
|
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmQuery
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
|
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
|
||||||
|
@ -25,8 +27,6 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmQuery
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is an helper around STATE_ROOM_MEMBER events.
|
* This class is an helper around STATE_ROOM_MEMBER events.
|
||||||
|
|
|
@ -21,21 +21,16 @@ import androidx.lifecycle.Transformations
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleScope
|
import org.matrix.android.sdk.api.pushrules.RuleScope
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService
|
import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.database.model.PushRuleEntity
|
import org.matrix.android.sdk.internal.database.model.PushRuleEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
|
|
||||||
internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String,
|
internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
private val setRoomNotificationStateTask: SetRoomNotificationStateTask,
|
private val setRoomNotificationStateTask: SetRoomNotificationStateTask,
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy)
|
||||||
private val taskExecutor: TaskExecutor)
|
|
||||||
: RoomPushRuleService {
|
: RoomPushRuleService {
|
||||||
|
|
||||||
@AssistedInject.Factory
|
@AssistedInject.Factory
|
||||||
|
@ -49,12 +44,8 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setRoomNotificationState(roomNotificationState: RoomNotificationState, matrixCallback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) {
|
||||||
return setRoomNotificationStateTask
|
setRoomNotificationStateTask.execute(SetRoomNotificationStateTask.Params(roomId, roomNotificationState))
|
||||||
.configureWith(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) {
|
|
||||||
this.callback = matrixCallback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPushRuleForRoom(): LiveData<RoomPushRule?> {
|
private fun getPushRuleForRoom(): LiveData<RoomPushRule?> {
|
||||||
|
|
|
@ -18,14 +18,9 @@ package org.matrix.android.sdk.internal.session.room.reporting
|
||||||
|
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.room.reporting.ReportingService
|
import org.matrix.android.sdk.api.session.room.reporting.ReportingService
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
|
||||||
|
|
||||||
internal class DefaultReportingService @AssistedInject constructor(@Assisted private val roomId: String,
|
internal class DefaultReportingService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
private val reportContentTask: ReportContentTask
|
private val reportContentTask: ReportContentTask
|
||||||
) : ReportingService {
|
) : ReportingService {
|
||||||
|
|
||||||
|
@ -34,13 +29,8 @@ internal class DefaultReportingService @AssistedInject constructor(@Assisted pri
|
||||||
fun create(roomId: String): ReportingService
|
fun create(roomId: String): ReportingService
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reportContent(eventId: String, score: Int, reason: String, callback: MatrixCallback<Unit>): Cancelable {
|
override suspend fun reportContent(eventId: String, score: Int, reason: String) {
|
||||||
val params = ReportContentTask.Params(roomId, eventId, score, reason)
|
val params = ReportContentTask.Params(roomId, eventId, score, reason)
|
||||||
|
reportContentTask.execute(params)
|
||||||
return reportContentTask
|
|
||||||
.configureWith(params) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
|
|
||||||
internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
|
internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||||
private val stateEventDataSource: StateEventDataSource,
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
|
@ -132,23 +133,23 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
||||||
override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable {
|
override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg")
|
val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg")
|
||||||
|
awaitCallback<Unit> {
|
||||||
sendStateEvent(
|
sendStateEvent(
|
||||||
eventType = EventType.STATE_ROOM_AVATAR,
|
eventType = EventType.STATE_ROOM_AVATAR,
|
||||||
body = mapOf("url" to response.contentUri),
|
body = mapOf("url" to response.contentUri),
|
||||||
callback = callback,
|
callback = it,
|
||||||
stateKey = null
|
stateKey = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable {
|
override fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable {
|
||||||
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
return sendStateEvent(
|
||||||
sendStateEvent(
|
|
||||||
eventType = EventType.STATE_ROOM_AVATAR,
|
eventType = EventType.STATE_ROOM_AVATAR,
|
||||||
body = emptyMap(),
|
body = emptyMap(),
|
||||||
callback = callback,
|
callback = callback,
|
||||||
stateKey = null
|
stateKey = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue