Merge branch 'develop' into feature/fga/voip_v1_start

This commit is contained in:
ganfra 2020-12-03 13:01:54 +01:00
commit efec711ced
458 changed files with 11172 additions and 4630 deletions

View file

@ -31,6 +31,7 @@
<w>ssss</w> <w>ssss</w>
<w>sygnal</w> <w>sygnal</w>
<w>threepid</w> <w>threepid</w>
<w>unpublish</w>
<w>unwedging</w> <w>unwedging</w>
</words> </words>
</dictionary> </dictionary>

View file

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

View file

@ -1,25 +1,73 @@
Changes in Element 1.0.11 (2020-XX-XX) Changes in Element 1.0.12 (2020-XX-XX)
=================================================== ===================================================
Features ✨: Features ✨:
- - Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428)
- Room setting: update join rules and guest access (#2442)
Improvements 🙌: Improvements 🙌:
- Open an existing DM instead of creating a new one (#2319) - Add Setting Item to Change PIN (#2462)
- Improve room history visibility setting UX (#1579)
Bugfix 🐛: Bugfix 🐛:
- Fix issue when updating the avatar of a room (new avatar vanishing) - Double bottomsheet effect after verify with passphrase
- Discard change dialog displayed by mistake when avatar has been updated - EditText cursor jumps to the start while typing fast (#2469)
Translations 🗣: Translations 🗣:
- -
SDK API changes ⚠️: 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. -
Build 🧱: Build 🧱:
- Upgrade some dependencies and Kotlin version
- Use fragment-ktx and preference-ktx dependencies (fix lint issue KtxExtensionAvailable)
Test:
- -
Other changes:
- Remove "Status.im" theme #2424
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: Other changes:
- Upgrade Realm dependency to 10.0.0 - Upgrade Realm dependency to 10.0.0
@ -1033,5 +1081,8 @@ SDK API changes ⚠️:
Build 🧱: Build 🧱:
- -
Test:
-
Other changes: Other changes:
- -

View file

@ -66,7 +66,6 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation "androidx.fragment:fragment:1.3.0-beta01"
implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'com.google.android.material:material:1.2.1' implementation 'com.google.android.material:material:1.2.1'

View file

@ -2,8 +2,8 @@
buildscript { buildscript {
// Ref: https://kotlinlang.org/releases.html // Ref: https://kotlinlang.org/releases.html
ext.kotlin_version = '1.4.10' ext.kotlin_version = '1.4.20'
ext.kotlin_coroutines_version = "1.3.9" ext.kotlin_coroutines_version = "1.4.1"
repositories { repositories {
google() google()
jcenter() jcenter()
@ -12,7 +12,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.0' classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'com.google.gms:google-services:4.3.4' classpath 'com.google.gms:google-services:4.3.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1' classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'

View file

@ -0,0 +1 @@
// TODO

View 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.

View file

@ -0,0 +1 @@
Xat i VoIP segurs i descentralitzats. Protegeix les teves dades de tercers.

View file

@ -0,0 +1 @@
Element (anteriorment Riot.im)

View file

@ -0,0 +1 @@
// TODO

View 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

View file

@ -0,0 +1 @@
// TODO

View file

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

View file

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

View file

@ -1 +1 @@
Element (anteriorment Riot.im) Element (previamente Riot.im)

View file

@ -0,0 +1 @@
// برای انجام

View file

@ -0,0 +1 @@
// DA FARE

View file

@ -0,0 +1 @@
Sikker desentralisert chat & VoIP. Beskytt dataene dine fra tredjeparter.

View file

@ -0,0 +1 @@
Element (tidligere Riot.im)

View file

@ -0,0 +1 @@
// A FAZER

View file

@ -0,0 +1 @@
// ATT GÖRA

View file

@ -0,0 +1 @@
// 待辦事項

View 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 來在所有裝置與網路上保持訊息歷史同步。

View file

@ -0,0 +1 @@
安全的去中心化聊天與 VoIP。確保您的資料不受第三方的影響。

View file

@ -0,0 +1 @@
Element曾名為 Riot.im

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=0080de8491f0918e4f529a6db6820fa0b9e818ee2386117f4394f95feb1d5583 distributionSha256Sum=22449f5231796abd892c98b2a07c9ceebe4688d192cd2d6763f8e3bf8acbedeb
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

View file

@ -36,9 +36,9 @@ android {
dependencies { dependencies {
implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android")
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation "androidx.fragment:fragment:1.3.0-beta01"
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
// Paging // Paging
implementation "androidx.paging:paging-runtime-ktx:2.1.2" implementation "androidx.paging:paging-runtime-ktx:2.1.2"

View file

@ -21,34 +21,36 @@ import org.matrix.android.sdk.api.util.Cancelable
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
fun <T> singleBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Single<T> = Single.create { fun <T> singleBuilder(builder: (MatrixCallback<T>) -> Cancelable): Single<T> = Single.create { emitter ->
val callback: MatrixCallback<T> = object : MatrixCallback<T> { val callback = object : MatrixCallback<T> {
override fun onSuccess(data: T) { override fun onSuccess(data: T) {
it.onSuccess(data) // Add `!!` to fix the warning:
// "Type mismatch: type parameter with nullable bounds is used T is used where T was expected. This warning will become an error soon"
emitter.onSuccess(data!!)
} }
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
it.tryOnError(failure) emitter.tryOnError(failure)
} }
} }
val cancelable = builder(callback) val cancelable = builder(callback)
it.setCancellable { emitter.setCancellable {
cancelable.cancel() cancelable.cancel()
} }
} }
fun <T> completableBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Completable = Completable.create { fun <T> completableBuilder(builder: (MatrixCallback<T>) -> Cancelable): Completable = Completable.create { emitter ->
val callback: MatrixCallback<T> = object : MatrixCallback<T> { val callback = object : MatrixCallback<T> {
override fun onSuccess(data: T) { override fun onSuccess(data: T) {
it.onComplete() emitter.onComplete()
} }
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
it.tryOnError(failure) emitter.tryOnError(failure)
} }
} }
val cancelable = builder(callback) val cancelable = builder(callback)
it.setCancellable { emitter.setCancellable {
cancelable.cancel() cancelable.cancel()
} }
} }

View file

@ -35,6 +35,8 @@ import org.matrix.android.sdk.api.util.toOptional
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
class RxRoom(private val room: Room) { class RxRoom(private val room: Room) {
@ -127,18 +129,14 @@ class RxRoom(private val room: Room) {
room.updateName(name, it) room.updateName(name, it)
} }
fun addRoomAlias(alias: String): Completable = completableBuilder<Unit> {
room.addRoomAlias(alias, it)
}
fun updateCanonicalAlias(alias: String): Completable = completableBuilder<Unit> {
room.updateCanonicalAlias(alias, it)
}
fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = completableBuilder<Unit> { fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = completableBuilder<Unit> {
room.updateHistoryReadability(readability, it) room.updateHistoryReadability(readability, it)
} }
fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?): Completable = completableBuilder<Unit> {
room.updateJoinRule(joinRules, guestAccess, it)
}
fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> { fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> {
room.updateAvatar(avatarUri, fileName, it) room.updateAvatar(avatarUri, fileName, it)
} }

View file

@ -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()
} }

View file

@ -125,7 +125,6 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.fragment:fragment:1.3.0-beta01"
implementation "androidx.core:core-ktx:1.3.2" implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
@ -146,7 +145,7 @@ dependencies {
implementation "ru.noties.markwon:core:$markwon_version" implementation "ru.noties.markwon:core:$markwon_version"
// Image // Image
implementation 'androidx.exifinterface:exifinterface:1.3.0' implementation 'androidx.exifinterface:exifinterface:1.3.1'
// Database // Database
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'

View file

@ -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()
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()
} }

View file

@ -49,6 +49,12 @@ object EventType {
const val STATE_ROOM_JOIN_RULES = "m.room.join_rules" const val STATE_ROOM_JOIN_RULES = "m.room.join_rules"
const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access" const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access"
const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels" const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels"
/**
* Note that this Event has been deprecated, see
* - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events
* - https://github.com/matrix-org/matrix-doc/pull/2432
*/
const val STATE_ROOM_ALIASES = "m.room.aliases" const val STATE_ROOM_ALIASES = "m.room.aliases"
const val STATE_ROOM_TOMBSTONE = "m.room.tombstone" const val STATE_ROOM_TOMBSTONE = "m.room.tombstone"
const val STATE_ROOM_CANONICAL_ALIAS = "m.room.canonical_alias" const val STATE_ROOM_CANONICAL_ALIAS = "m.room.canonical_alias"
@ -56,6 +62,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"

View file

@ -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()
} }

View file

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

View file

@ -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()
} }

View file

@ -16,9 +16,6 @@
package org.matrix.android.sdk.api.session.integrationmanager package org.matrix.android.sdk.api.session.integrationmanager
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
/** /**
* This is the entry point to manage integration. You can grab an instance of this service through an active session. * This is the entry point to manage integration. You can grab an instance of this service through an active session.
*/ */
@ -80,19 +77,17 @@ interface IntegrationManagerService {
/** /**
* Offers to enable or disable the integration. * Offers to enable or disable the integration.
* @param enable the param to change * @param enable the param to change
* @param callback the matrix callback to listen for result.
* @return Cancelable * @return Cancelable
*/ */
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable suspend fun setIntegrationEnabled(enable: Boolean)
/** /**
* Offers to allow or disallow a widget. * Offers to allow or disallow a widget.
* @param stateEventId the eventId of the state event defining the widget. * @param stateEventId the eventId of the state event defining the widget.
* @param allowed the param to change * @param allowed the param to change
* @param callback the matrix callback to listen for result.
* @return Cancelable * @return Cancelable
*/ */
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean)
/** /**
* Returns true if the widget is allowed, false otherwise. * Returns true if the widget is allowed, false otherwise.
@ -105,7 +100,7 @@ interface IntegrationManagerService {
* @param widgetType the widget type to check for * @param widgetType the widget type to check for
* @param domain the domain to check for * @param domain the domain to check for
*/ */
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean)
/** /**
* Returns true if the widget domain is allowed, false otherwise. * Returns true if the widget domain is allowed, false otherwise.

View file

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

View file

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

View file

@ -18,6 +18,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.alias.AliasService
import org.matrix.android.sdk.api.session.room.call.RoomCallService import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.MembershipService
@ -46,6 +47,7 @@ interface Room :
DraftService, DraftService,
ReadService, ReadService,
TypingService, TypingService,
AliasService,
TagsService, TagsService,
MembershipService, MembershipService,
StateService, StateService,

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.api.session.room package org.matrix.android.sdk.api.session.room
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
@ -39,4 +40,14 @@ interface RoomDirectoryService {
* Includes both the available protocols and all fields required for queries against each protocol. * Includes both the available protocols and all fields required for queries against each protocol.
*/ */
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable
/**
* Get the visibility of a room in the directory
*/
suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility
/**
* Set the visibility of a room in the directory
*/
suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility)
} }

View file

@ -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
@ -121,6 +122,11 @@ interface RoomService {
searchOnServer: Boolean, searchOnServer: Boolean,
callback: MatrixCallback<Optional<String>>): Cancelable callback: MatrixCallback<Optional<String>>): Cancelable
/**
* Delete a room alias
*/
suspend fun deleteRoomAlias(roomAlias: String)
/** /**
* Return a live data of all local changes membership that happened since the session has been opened. * Return a live data of all local changes membership that happened since the session has been opened.
* It allows you to track this in your client to known what is currently being processed by the SDK. * It allows you to track this in your client to known what is currently being processed by the SDK.
@ -141,4 +147,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>>
} }

View file

@ -0,0 +1,32 @@
/*
* 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.alias
interface AliasService {
/**
* Get list of local alias of the room
* @return the list of the aliases (full aliases, not only the local part)
*/
suspend fun getRoomAliases(): List<String>
/**
* Add local alias to the room
* @param aliasLocalPart the local part of the alias.
* Ex: for the alias "#my_alias:example.org", the local part is "my_alias"
*/
suspend fun addAlias(aliasLocalPart: String)
}

View file

@ -0,0 +1,23 @@
/*
* 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.alias
sealed class RoomAliasError : Throwable() {
object AliasEmpty : RoomAliasError()
object AliasNotAvailable : RoomAliasError()
object AliasInvalid : RoomAliasError()
}

View file

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

View file

@ -18,8 +18,10 @@ package org.matrix.android.sdk.api.session.room.failure
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
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()
data class AliasError(val aliasError: RoomAliasError) : CreateRoomFailure()
} }

View file

@ -21,6 +21,9 @@ import com.squareup.moshi.JsonClass
/** /**
* Class representing the EventType.STATE_ROOM_ALIASES state event content * Class representing the EventType.STATE_ROOM_ALIASES state event content
* Note that this Event has been deprecated, see
* - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events
* - https://github.com/matrix-org/matrix-doc/pull/2432
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class RoomAliasesContent( data class RoomAliasesContent(

View file

@ -24,5 +24,14 @@ import com.squareup.moshi.JsonClass
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class RoomCanonicalAliasContent( data class RoomCanonicalAliasContent(
@Json(name = "alias") val canonicalAlias: String? = null /**
* The canonical alias for the room. If not present, null, or empty the room should be considered to have no canonical alias.
*/
@Json(name = "alias") val canonicalAlias: String? = null,
/**
* Alternative aliases the room advertises.
* This list can have aliases despite the alias field being null, empty, or otherwise not present.
*/
@Json(name = "alt_aliases") val alternativeAliases: List<String>? = null
) )

View file

@ -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 = "*"
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -21,7 +21,9 @@ import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
@ -38,21 +40,23 @@ interface StateService {
*/ */
fun updateName(name: String, callback: MatrixCallback<Unit>): Cancelable fun updateName(name: String, callback: MatrixCallback<Unit>): Cancelable
/**
* Add new alias to the room.
*/
fun addRoomAlias(roomAlias: String, callback: MatrixCallback<Unit>): Cancelable
/** /**
* Update the canonical alias of the room * Update the canonical alias of the room
* @param alias the canonical alias, or null to reset the canonical alias of this room
* @param altAliases the alternative aliases for this room. It should include the canonical alias if any.
*/ */
fun updateCanonicalAlias(alias: String, callback: MatrixCallback<Unit>): Cancelable fun updateCanonicalAlias(alias: String?, altAliases: List<String>, callback: MatrixCallback<Unit>): Cancelable
/** /**
* Update the history readability of the room * Update the history readability of the room
*/ */
fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback<Unit>): Cancelable fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback<Unit>): Cancelable
/**
* Update the join rule and/or the guest access
*/
fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback<Unit>): Cancelable
/** /**
* Update the avatar of the room * Update the avatar of the room
*/ */

View file

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

View file

@ -16,9 +16,6 @@
package org.matrix.android.sdk.api.session.room.uploads package org.matrix.android.sdk.api.session.room.uploads
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
/** /**
* This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level. * This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level.
*/ */
@ -29,7 +26,5 @@ interface UploadsService {
* @param numberOfEvents the expected number of events to retrieve. The result can contain less events. * @param numberOfEvents the expected number of events to retrieve. The result can contain less events.
* @param since token to get next page, or null to get the first page * @param since token to get next page, or null to get the first page
*/ */
fun getUploads(numberOfEvents: Int, suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult
since: String?,
callback: MatrixCallback<GetUploadsResult>): Cancelable
} }

View file

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

View file

@ -16,22 +16,16 @@
package org.matrix.android.sdk.api.session.terms package org.matrix.android.sdk.api.session.terms
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
interface TermsService { interface TermsService {
enum class ServiceType { enum class ServiceType {
IntegrationManager, IntegrationManager,
IdentityService IdentityService
} }
fun getTerms(serviceType: ServiceType, suspend fun getTerms(serviceType: ServiceType, baseUrl: String): GetTermsResponse
baseUrl: String,
callback: MatrixCallback<GetTermsResponse>): Cancelable
fun agreeToTerms(serviceType: ServiceType, suspend fun agreeToTerms(serviceType: ServiceType,
baseUrl: String, baseUrl: String,
agreedUrls: List<String>, agreedUrls: List<String>,
token: String?, token: String?)
callback: MatrixCallback<Unit>): Cancelable
} }

View file

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

View file

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

View file

@ -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}")
}
} }
} }
} }

View file

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

View file

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

View file

@ -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.")
} }
} }

View file

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

View file

@ -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")
}
} }
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -1204,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() }

View file

@ -28,10 +28,10 @@ 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
import java.util.Locale
/** /**
* Represents an ongoing short code interactive key verification between two devices. * Represents an ongoing short code interactive key verification between two devices.
@ -250,7 +250,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 +273,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)
@ -345,7 +345,7 @@ internal abstract class SASDefaultVerificationTransaction(
} }
protected fun hashUsingAgreedHashMethod(toHash: String): String? { protected fun hashUsingAgreedHashMethod(toHash: String): String? {
if ("sha256".toLowerCase() == accepted?.hash?.toLowerCase()) { if ("sha256" == accepted?.hash?.toLowerCase(Locale.ROOT)) {
val olmUtil = OlmUtility() val olmUtil = OlmUtility()
val hashBytes = olmUtil.sha256(toHash) val hashBytes = olmUtil.sha256(toHash)
olmUtil.releaseUtility() olmUtil.releaseUtility()
@ -355,12 +355,11 @@ internal abstract class SASDefaultVerificationTransaction(
} }
private fun macUsingAgreedMethod(message: String, info: String): String? { private fun macUsingAgreedMethod(message: String, info: String): String? {
if (SAS_MAC_SHA256_LONGKDF.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) { return when (accepted?.messageAuthenticationCode?.toLowerCase(Locale.ROOT)) {
return getSAS().calculateMacLongKdf(message, info) SAS_MAC_SHA256_LONGKDF -> getSAS().calculateMacLongKdf(message, info)
} else if (SAS_MAC_SHA256.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) { SAS_MAC_SHA256 -> getSAS().calculateMac(message, info)
return getSAS().calculateMac(message, info) else -> null
} }
return null
} }
override fun getDecimalCodeRepresentation(): String { override fun getDecimalCodeRepresentation(): String {

View file

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

View file

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

View file

@ -0,0 +1,70 @@
/*
* 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.directory
import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasBody
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.PUT
import retrofit2.http.Path
internal interface DirectoryAPI {
/**
* Get the room ID associated to the room alias.
*
* @param roomAlias the room alias.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call<RoomAliasDescription>
/**
* Get the room directory visibility.
*
* @param roomId the room id.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}")
fun getRoomDirectoryVisibility(@Path("roomId") roomId: String): Call<RoomDirectoryVisibilityJson>
/**
* Set the room directory visibility.
*
* @param roomId the room id.
* @param body the body containing the new directory visibility
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}")
fun setRoomDirectoryVisibility(@Path("roomId") roomId: String,
@Body body: RoomDirectoryVisibilityJson): Call<Unit>
/**
* Add alias to the room.
* @param roomAlias the room alias.
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
fun addRoomAlias(@Path("roomAlias") roomAlias: String,
@Body body: AddRoomAliasBody): Call<Unit>
/**
* Delete a room alias
* @param roomAlias the room alias.
*/
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
fun deleteRoomAlias(@Path("roomAlias") roomAlias: String): Call<Unit>
}

View file

@ -0,0 +1,29 @@
/*
* 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.directory
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
@JsonClass(generateAdapter = true)
internal data class RoomDirectoryVisibilityJson(
/**
* The visibility of the room in the directory. One of: ["private", "public"]
*/
@Json(name = "visibility") val visibility: RoomDirectoryVisibility
)

View file

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

View file

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

View file

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

View file

@ -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,8 @@ 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) .allowWritesOnUiThread(true)
.modules(IdentityRealmModule()) .modules(IdentityRealmModule())
.build() .build()

View file

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

View file

@ -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)
/** /**

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -16,10 +16,8 @@
package org.matrix.android.sdk.internal.session.integrationmanager package org.matrix.android.sdk.internal.session.integrationmanager
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
import org.matrix.android.sdk.api.util.Cancelable
import javax.inject.Inject import javax.inject.Inject
internal class DefaultIntegrationManagerService @Inject constructor(private val integrationManager: IntegrationManager) : IntegrationManagerService { internal class DefaultIntegrationManagerService @Inject constructor(private val integrationManager: IntegrationManager) : IntegrationManagerService {
@ -44,20 +42,20 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val
return integrationManager.isIntegrationEnabled() return integrationManager.isIntegrationEnabled()
} }
override fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable { override suspend fun setIntegrationEnabled(enable: Boolean) {
return integrationManager.setIntegrationEnabled(enable, callback) integrationManager.setIntegrationEnabled(enable)
} }
override fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable { override suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) {
return integrationManager.setWidgetAllowed(stateEventId, allowed, callback) integrationManager.setWidgetAllowed(stateEventId, allowed)
} }
override fun isWidgetAllowed(stateEventId: String): Boolean { override fun isWidgetAllowed(stateEventId: String): Boolean {
return integrationManager.isWidgetAllowed(stateEventId) return integrationManager.isWidgetAllowed(stateEventId)
} }
override fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable { override suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) {
return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed, callback) integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed)
} }
override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean { override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean {

View file

@ -20,15 +20,12 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.LifecycleRegistry
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.extensions.observeNotNull import org.matrix.android.sdk.internal.extensions.observeNotNull
@ -41,7 +38,6 @@ import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccoun
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
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 timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -137,22 +133,17 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
return integrationProvisioningContent?.enabled ?: false return integrationProvisioningContent?.enabled ?: false
} }
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable { suspend fun setIntegrationEnabled(enable: Boolean) {
val isIntegrationEnabled = isIntegrationEnabled() val isIntegrationEnabled = isIntegrationEnabled()
if (enable == isIntegrationEnabled) { if (enable == isIntegrationEnabled) {
callback.onSuccess(Unit) return
return NoOpCancellable
} }
val integrationProvisioningContent = IntegrationProvisioningContent(enabled = enable) val integrationProvisioningContent = IntegrationProvisioningContent(enabled = enable)
val params = UpdateUserAccountDataTask.IntegrationProvisioning(integrationProvisioningContent = integrationProvisioningContent) val params = UpdateUserAccountDataTask.IntegrationProvisioning(integrationProvisioningContent = integrationProvisioningContent)
return updateUserAccountDataTask return updateUserAccountDataTask.execute(params)
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
} }
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable { suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) {
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS) val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS)
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>() val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
val newContent = if (currentContent == null) { val newContent = if (currentContent == null) {
@ -165,11 +156,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
currentContent.copy(widgets = allowedWidgets) currentContent.copy(widgets = allowedWidgets)
} }
val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent) val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent)
return updateUserAccountDataTask return updateUserAccountDataTask.execute(params)
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
} }
fun isWidgetAllowed(stateEventId: String): Boolean { fun isWidgetAllowed(stateEventId: String): Boolean {
@ -178,7 +165,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
return currentContent?.widgets?.get(stateEventId) ?: false return currentContent?.widgets?.get(stateEventId) ?: false
} }
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable { suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) {
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS) val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS)
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>() val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
val newContent = if (currentContent == null) { val newContent = if (currentContent == null) {
@ -195,11 +182,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
currentContent.copy(native = nativeAllowedWidgets) currentContent.copy(native = nativeAllowedWidgets)
} }
val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent) val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent)
return updateUserAccountDataTask return updateUserAccountDataTask.execute(params)
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
} }
fun isNativeWidgetDomainAllowed(widgetType: String, domain: String?): Boolean { fun isNativeWidgetDomainAllowed(widgetType: String, domain: String?): Boolean {

View file

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

View file

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

View file

@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
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.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.alias.AliasService
import org.matrix.android.sdk.api.session.room.call.RoomCallService import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.MembershipService
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -58,6 +59,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
private val roomCallService: RoomCallService, private val roomCallService: RoomCallService,
private val readService: ReadService, private val readService: ReadService,
private val typingService: TypingService, private val typingService: TypingService,
private val aliasService: AliasService,
private val tagsService: TagsService, private val tagsService: TagsService,
private val cryptoService: CryptoService, private val cryptoService: CryptoService,
private val relationService: RelationService, private val relationService: RelationService,
@ -76,6 +78,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
RoomCallService by roomCallService, RoomCallService by roomCallService,
ReadService by readService, ReadService by readService,
TypingService by typingService, TypingService by typingService,
AliasService by aliasService,
TagsService by tagsService, TagsService by tagsService,
RelationService by relationService, RelationService by relationService,
MembershipService by roomMembersService, MembershipService by roomMembersService,
@ -101,13 +104,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 +121,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)
} }
} }
} }

View file

@ -18,18 +18,24 @@ package org.matrix.android.sdk.internal.session.room
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomDirectoryService
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
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 javax.inject.Inject import javax.inject.Inject
internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask, internal class DefaultRoomDirectoryService @Inject constructor(
private val getPublicRoomTask: GetPublicRoomTask,
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask, private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask,
private val taskExecutor: TaskExecutor) : RoomDirectoryService { private val taskExecutor: TaskExecutor) : RoomDirectoryService {
override fun getPublicRooms(server: String?, override fun getPublicRooms(server: String?,
@ -49,4 +55,12 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu
} }
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }
override suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility {
return getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))
}
override suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) {
setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility))
}
} }

View file

@ -17,32 +17,44 @@
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.DeleteRoomAliasTask
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,
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
private val roomIdByAliasTask: GetRoomIdByAliasTask, private val roomIdByAliasTask: GetRoomIdByAliasTask,
private val deleteRoomAliasTask: DeleteRoomAliasTask,
private val roomGetter: RoomGetter, private val roomGetter: RoomGetter,
private val roomSummaryDataSource: RoomSummaryDataSource, private val roomSummaryDataSource: RoomSummaryDataSource,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
@ -115,7 +127,31 @@ internal class DefaultRoomService @Inject constructor(
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }
override suspend fun deleteRoomAlias(roomAlias: String) {
deleteRoomAliasTask.execute(DeleteRoomAliasTask.Params(roomAlias))
}
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()
}
}
} }

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