Merge branch 'develop' into feature/ons/device_manager_current_session

* develop: (169 commits)
  new app layouts invites (#6911)
  bottom navigation tabs are removed for AppLayout (#6905)
  focus input when changing server address
  Fix copyright
  Improve createRoomThreePidEvents for clarity
  Remove roomCreatorUserId and use current userId by default
  Remove useless explicit field type
  Change visibility of LocalRoomThirdPartyInviteContent to internal
  Remove useless apply in CreateLocalRoomStateEventsTask
  Update doc
  Extract condition to reduce code complexity
  Verify tombstone event
  Remove safe call
  Add unit tests for CreateRoomFromLocalRoomTask
  Add unit tests for CreateLocalRoomStateEventsTask
  Set stateKey as empty by default
  Create local room state events in dedicated task
  Fix local events generation following the specification
  Update CreateRoomParams from the potential FeaturePreset before persisting
  Persists CreateRoomParams into LocalRoomSummaryEntity
  ...

# Conflicts:
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
This commit is contained in:
Onuray Sahin 2022-08-26 13:45:47 +03:00
commit 7a3a5ae30d
489 changed files with 5935 additions and 9248 deletions

View File

@ -21,6 +21,8 @@ body:
- [ ] While Weblate is locked, and after the PR from Weblate has been merged, handle all the TODOs in the main `strings.xml` file
- [ ] Run the script `./tools/release/pushPlayStoreMetaData.sh`. You can check in the GooglePlay console the Activity log to check the effect.
- [ ] Ensure all [the required PRs](https://github.com/vector-im/element-android/pulls?q=is%3Aopen+is%3Apr+label%3AZ-NextRelease) have been merged
### Do the release
- [ ] Make sure `develop` and `main` are up to date (git pull)

View File

@ -11,8 +11,10 @@ jobs:
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
uses: danger/danger-js@11.1.1
uses: danger/danger-js@11.1.2
with:
args: "--dangerfile tools/danger/dangerfile.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -66,11 +66,13 @@ jobs:
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
uses: danger/danger-js@11.1.1
uses: danger/danger-js@11.1.2
with:
args: "--dangerfile tools/danger/dangerfile-lint.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Gradle dependency analysis using https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin
dependency-analysis:

View File

@ -98,7 +98,8 @@ jobs:
# Skip in forks
if: >
github.repository == 'vector-im/element-android' &&
(contains(github.event.issue.labels.*.name, 'Team: Delight'))
(contains(github.event.issue.labels.*.name, 'Team: Delight') ||
contains(github.event.issue.labels.*.name, 'Z-AppLayout'))
steps:
- uses: octokit/graphql-action@v2.x
with:

View File

@ -1,3 +1,51 @@
Changes in Element v1.4.34 (2022-08-23)
=======================================
Features ✨
----------
- [Notification] - Handle creation of notification for live location and poll start ([#6746](https://github.com/vector-im/element-android/issues/6746))
Bugfixes 🐛
----------
- Fixes onboarding requiring matrix.org to be accessible on the first step, the server can now be manually changed ([#6718](https://github.com/vector-im/element-android/issues/6718))
- Fixing sign in/up for homeservers that rely on the SSO fallback url ([#6827](https://github.com/vector-im/element-android/issues/6827))
- Fixes uncaught exceptions in the SyncWorker to cause the worker to become stuck in the failure state ([#6836](https://github.com/vector-im/element-android/issues/6836))
- Fixes onboarding captcha crashing when no WebView is available by showing an error with information instead ([#6855](https://github.com/vector-im/element-android/issues/6855))
- Removes ability to continue registration after the app has been destroyed, fixes the next steps crashing due to missing information from the previous steps ([#6860](https://github.com/vector-im/element-android/issues/6860))
- Fixes crash when exiting the login or registration entry screens whilst they're loading ([#6861](https://github.com/vector-im/element-android/issues/6861))
- Fixes server selection being unable to trust certificates ([#6864](https://github.com/vector-im/element-android/issues/6864))
- Ensure SyncThread is started when the app is launched after a Push has been received. ([#6884](https://github.com/vector-im/element-android/issues/6884))
- Fixes missing firebase notifications after logging in when UnifiedPush distributor is installed ([#6891](https://github.com/vector-im/element-android/issues/6891))
In development 🚧
----------------
- Create DM room only on first message - Trigger the flow when the "Direct Message" action is selected from the room member details screen ([#5525](https://github.com/vector-im/element-android/issues/5525))
- added filter tabs for new App layout's Home screen ([#6505](https://github.com/vector-im/element-android/issues/6505))
- [App Layout] added dialog to configure app layout ([#6506](https://github.com/vector-im/element-android/issues/6506))
- Adds space list bottom sheet for new app layout ([#6749](https://github.com/vector-im/element-android/issues/6749))
- [App Layout] Dialpad moved from bottom navigation tab to a separate activity accessed via home screen context menu ([#6787](https://github.com/vector-im/element-android/issues/6787))
- Makes toolbar switch title based on space in New App Layout ([#6795](https://github.com/vector-im/element-android/issues/6795))
- [Devices management] Add a feature flag and empty screen for future new layout ([#6798](https://github.com/vector-im/element-android/issues/6798))
- Adds new chat bottom sheet as the click action of the main FAB in the new app layout ([#6801](https://github.com/vector-im/element-android/issues/6801))
- [Devices management] Other sessions section in new layout ([#6806](https://github.com/vector-im/element-android/issues/6806))
- [New Layout] Adds space settings accessible through clicking the toolbar ([#6859](https://github.com/vector-im/element-android/issues/6859))
- Adds New App Layout FABs (hidden behind feature flag) ([#6693](https://github.com/vector-im/element-android/issues/6693))
SDK API changes ⚠️
------------------
- Rename `DebugService.logDbUsageInfo` (resp. `Session.logDbUsageInfo`) to `DebugService.getDbUsageInfo` (resp. `Session.getDbUsageInfo`) and return a String instead of logging. The caller may want to log the String. ([#6884](https://github.com/vector-im/element-android/issues/6884))
Other changes
-------------
- Removes the Login2 proof of concept - replaced by the FTUE changes ([#5974](https://github.com/vector-im/element-android/issues/5974))
- Enable auto-capitalization for Room creation Title field ([#6645](https://github.com/vector-im/element-android/issues/6645))
- Decouples the variant logic from the vector module ([#6783](https://github.com/vector-im/element-android/issues/6783))
- Add a developer setting to enable LeakCanary at runtime ([#6786](https://github.com/vector-im/element-android/issues/6786))
- [Create Room] Reduce some boilerplate with room state event contents ([#6799](https://github.com/vector-im/element-android/issues/6799))
- [Call] Memory leak after a call ([#6808](https://github.com/vector-im/element-android/issues/6808))
- Fix some string template ([#6843](https://github.com/vector-im/element-android/issues/6843))
Changes in Element v1.4.32 (2022-08-10)
=======================================

View File

@ -24,12 +24,12 @@ buildscript {
classpath libs.gradle.gradlePlugin
classpath libs.gradle.kotlinPlugin
classpath libs.gradle.hiltPlugin
classpath 'com.google.firebase:firebase-appdistribution-gradle:3.0.2'
classpath 'com.google.firebase:firebase-appdistribution-gradle:3.0.3'
classpath 'com.google.gms:google-services:4.3.13'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
classpath "com.likethesalad.android:stem-plugin:2.1.1"
classpath 'org.owasp:dependency-check-gradle:7.1.1'
classpath 'org.owasp:dependency-check-gradle:7.1.2'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
// NOTE: Do not place your application dependencies here; they belong
@ -44,7 +44,7 @@ plugins {
id "io.gitlab.arturbosch.detekt" version "1.21.0"
// Dependency Analysis
id 'com.autonomousapps.dependency-analysis' version "1.11.2"
id 'com.autonomousapps.dependency-analysis' version "1.12.0"
}
// https://github.com/jeremylong/DependencyCheck
@ -151,6 +151,8 @@ allprojects {
"experimental:comment-wrapping",
// - A KDoc comment after any other element on the same line must be separated by a new line
"experimental:kdoc-wrapping",
// Ignore error "Redundant curly braces", since we use it to fix false positives, for instance in "elementLogs.${i}.txt"
"string-template",
]
}

1
changelog.d/5525.wip Normal file
View File

@ -0,0 +1 @@
Create DM room only on first message - Create the DM and navigate to the new room after sending an event

View File

@ -1 +0,0 @@
added filter tabs for new App layout's Home screen

1
changelog.d/6565.wip Normal file
View File

@ -0,0 +1 @@
[App Layout] Bottom navigation tabs are removed for new home screen

View File

@ -1 +0,0 @@
Enable auto-capitalization for Room creation Title field

View File

@ -1 +0,0 @@
Adds New App Layout FABs (hidden behind feature flag)

View File

@ -1 +0,0 @@
[Notification] - Handle creation of notification for live location and poll start

1
changelog.d/6750.wip Normal file
View File

@ -0,0 +1 @@
[App Layout] fixed space switching dialog measured with wrong height sometimes

View File

@ -1 +0,0 @@
Decouples the variant logic from the vector module

View File

@ -1 +0,0 @@
Add a developer setting to enable LeakCanary at runtime

View File

@ -1 +0,0 @@
[Devices management] Add a feature flag and empty screen for future new layout

View File

@ -1 +0,0 @@
[Create Room] Reduce some boilerplate with room state event contents

View File

@ -1 +0,0 @@
[Devices management] Other sessions section in new layout

View File

@ -1 +0,0 @@
[Call] Memory leak after a call

1
changelog.d/6889.wip Normal file
View File

@ -0,0 +1 @@
[App Layout] new room invites screen

1
changelog.d/6894.misc Normal file
View File

@ -0,0 +1 @@
Remove FragmentModule and the Fragment factory. No need to Inject the constructor on your Fragment, just add @AndroidEntryPoint annotation and @Inject class members.

1
changelog.d/6926.misc Normal file
View File

@ -0,0 +1 @@
Focus input field when editing homeserver address to speed up login and registration.

View File

@ -22,7 +22,7 @@ def markwon = "4.6.2"
def moshi = "1.13.0"
def lifecycle = "2.5.1"
def flowBinding = "1.2.0"
def flipper = "0.156.0"
def flipper = "0.161.0"
def epoxy = "4.6.2"
def mavericks = "2.7.0"
def glide = "4.13.2"
@ -30,7 +30,7 @@ def bigImageViewer = "1.8.1"
def jjwt = "0.11.5"
def vanniktechEmoji = "0.15.0"
def fragment = "1.5.1"
def fragment = "1.5.2"
// Testing
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
@ -104,6 +104,7 @@ ext.libs = [
'moshi' : "com.squareup.moshi:moshi:$moshi",
'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi",
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
'moshiAdapters' : "com.squareup.moshi:moshi-adapters:$moshi",
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
],

View File

@ -85,6 +85,8 @@ To let Danger check all the PRs, including PRs form forks, a GitHub account have
- password: Stored on Passbolt
- GitHub token: A token with limited access has been created and added to the repository https://github.com/vector-im/element-android as secret DANGER_GITHUB_API_TOKEN. This token is not saved anywhere else. In case of problem, just delete it and create a new one, then update the secret.
PRs from forks do not always have access to the secret `secrets.DANGER_GITHUB_API_TOKEN`, so `secrets.GITHUB_TOKEN` is also provided to the job environment. If `secrets.DANGER_GITHUB_API_TOKEN` is available, it will be used, so user `ElementBot` will comment the PR. Else `secrets.GITHUB_TOKEN` will be used, and bot `github-actions` will comment the PR.
## Useful links
- https://danger.systems/

View File

@ -7,8 +7,8 @@ Hilt is built on top of Dagger 2 and simplify usage by removing needs to create
When you create a new feature, you should have the following:
Annotate your Activity with @AndroidEntryPoint
Annotate your Fragment with @AndroidEntryPoint
If you have a BottomSheetFragment => Annotate it with @AndroidEntryPoint
Otherwise => Add your Fragment to the FragmentModule
Add your ViewModel.Factory to the MavericksViewModelModule
Makes sure your ViewModel as the following code:

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Umožňuje vylepšené přihlašování a registraci.
Úplný seznam změn: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Umožňuje vylepšené přihlašování a registraci.
Úplný seznam změn: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Main changes in this version: Various bug fixes and stability improvements.
Full changelog: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: senisest parem liitumise ja sisselogimise töövoog.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: senisest parem liitumise ja sisselogimise töövoog.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
تغییرات عمده در این نگارش: به کار انداختن ورود بهبود یافته و سفرهای ورود.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
تغییرات عمده در این نگارش: به کار انداختن ورود بهبود یافته و سفرهای ورود.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : Activation de lauthentification et du parcours dinscription améliorés.
Intégralité des changements : https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : Activation de lauthentification et du parcours dinscription améliorés.
Intégralité des changements : https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Perubahan utama dalam versi ini: Mengaktifkan perjalanan masuk dan keluar yang diperbaiki.
Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Perubahan utama dalam versi ini: Mengaktifkan perjalanan masuk dan keluar yang diperbaiki.
Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases

View File

@ -1,42 +1,42 @@
Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi perpesanan ini menggunakan enkripsi ujung-ke-ujung untuk memberikan konferensi video, pembagian file, dan panggilan suara yang aman.
Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi perpesanan ini menggunakan enkripsi ujung-ke-ujung untuk menyediakan konferensi video, pembagian berkas, dan panggilan suara yang aman.
<b>Fitur Element termasuk</b>
- Alat komunikasi online yang canggih
<b>Fitur Element termasuk:</b>
- Alat komunikasi daring yang canggih
- Pesan-pesan yang dienkripsi sepenuhnya untuk memungkinkan komunikasi perusahaan yang lebih aman, bahkan untuk pekerja jarak jauh
- Obrolan terdesentralisasi berdasarkan kerangka Matrix yang sumber terbuka
- Pembagian file aman dengan data terenkripsi saat mengelola proyek
- Obrolan terdesentralisasi berdasarkan kerangka kerja Matrix yang sumber terbuka
- Pembagian berkas aman dengan data terenkripsi saat mengelola proyek
- Obrolan video dengan VoIP dan pembagian layar
- Integrasi yang mudah dengan alat kolaborasi online favorit Anda, alat manajemen proyek, layanan VoIP dan aplikasi perpesanan tim lainnya
- Integrasi yang mudah dengan alat kolaborasi daring favorit Anda, alat pengelola proyek, layanan VoIP dan aplikasi perpesanan tim lainnya
Element benar-benar berbeda dari aplikasi perpesanan dan aplikasi kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi.
Element benar-benar berbeda dari aplikasi perpesanan dan aplikasi kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi yang terdesentralisasi.
<b>Perpesanan dengan privasi dan enkripsi</b>
Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu dengan enkripsi ujung-ke-ujung dan verifikasi perangkat menggunakan penandatanganan silang.
Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data, dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu dengan enkripsi ujung-ke-ujung, dan verifikasi perangkat menggunakan penandatanganan silang.
Element memberikan Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan siapa saja secara aman di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan aplikasi-aplikasi seperti Slack.
Element memberikan Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan siapa saja secara aman di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan aplikasi seperti Slack.
<b>Element dapat dihost sendiri</b>
Untuk memungkinkan lebih banyak kendali atas data dan pesan-pesan sensitif Anda, Element dapat dihost sendiri atau Anda dapat memilih host berbasis Matrix, standar untuk komunikasi terdesentralisasi sumber terbuka. Element memberi Anda privasi, kepatuhan keamanan, dan fleksibilitas integrasi.
<b>Element dapat di-host sendiri</b>
Untuk memungkinkan lebih banyak kendali atas data dan pesan-pesan sensitif Anda, Element dapat dilayani sendiri atau Anda dapat memilih layanan berbasis Matrix, standar untuk komunikasi terdesentralisasi sumber terbuka. Element memberikan Anda privasi, kepatuhan keamanan, dan fleksibilitas integrasi.
<b>Miliki data Anda</b>
Anda memutuskan di mana untuk menyimpan data dan pesan-pesan Anda, tanpa risiko penambangan data atau akses dari pihak ketiga.
Element menempatkan Anda dalam kendali dengan cara yang berbeda:
1. Dapatkan akun gratis pada server publik matrix.org yang dihost oleh pengembang Matrix, atau memilih dari ribuan server publik yang dihost oleh sukarelawan
2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri
1. Dapatkan akun gratis pada server publik matrix.org yang dilayani oleh pengembang Matrix, atau memilih dari ribuan server publik yang dilayani oleh sukarelawan
2. Layani akun Anda sendiri dengan menjalankan server pada infrastruktur IT Anda sendiri
3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element
<b>Perpesanan dan kolaborasi terbuka</b>
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda.
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain, atau bahkan menggunakan aplikasi perpesanan yang berbeda.
<b>Sangat aman</b>
Enkripsi ujung-ke-ujung yang nyata (hanya mereka yang dalam obrolan dapat mendekripsi pesan), dan verifikasi perangkat menggunakan penandatanganan silang.
Enkripsi ujung-ke-ujung yang nyata (hanya mereka yang di dalam obrolan dapat mendekripsikan pesan), dan verifikasi perangkat menggunakan penandatanganan silang.
<b>Komunikasi dan integrasi lengkap</b>
Perpesanan, panggilan suara dan video, pembagian file, pembagian layar dan banyak integrasi bot dan widget. Buat ruangan dan komunitas, tetap terhubung dan selesaikan hal-hal penting.
Perpesanan, panggilan suara dan video, pembagian berkas, pembagian layar dan banyak integrasi bot dan widget. Buat ruangan dan komunitas, tetap terhubung, dan selesaikan hal-hal penting.
<b>Ambil di mana Anda tinggalkan</b>
Tetap terhubung di mana Anda berada, dengan riwayat pesan yang disinkronkan di semua perangkat Anda dan web di https://app.element.io
Tetap terhubung di mana Anda berada, dengan riwayat pesan yang disinkronkan pada semua perangkat Anda dan pada web di https://app.element.io
<b>Sumber terbuka</b>
Element Android adalah proyek sumber terbuka, dihost oleh GitHub. Silakan laporkan masalah yang Anda temukan, atau membuat kontribusi ke pengembangannya di https://github.com/vector-im/element-android
Element Android adalah proyek sumber terbuka, dilayani oleh GitHub. Silakan laporkan masalah yang Anda temukan, atau membuat kontribusi ke pengembangannya di https://github.com/vector-im/element-android

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: introduce i percorsi migliorati di accesso e registrazione.
Cronologia completa: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: introduce i percorsi migliorati di accesso e registrazione.
Cronologia completa: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Habilita as jornadas melhoradas de sign in e sign up.
Changelog completo: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Habilita as jornadas melhoradas de sign in e sign up.
Changelog completo: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Umožňuje vylepšené postupy prihlasovania a registrácie.
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Umožňuje vylepšené postupy prihlasovania a registrácie.
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Основні зміни в цій версії: Поліпшені вхід і реєстрація.
Перелік усіх змін: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
Основні зміни в цій версії: Поліпшені вхід і реєстрація.
Перелік усіх змін: https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
此版本中的主要變動:啟用改善的登入與註冊流程。
完整的變更紀錄https://github.com/vector-im/element-android/releases

View File

@ -0,0 +1,2 @@
此版本中的主要變動:啟用改善的登入與註冊流程。
完整的變更紀錄https://github.com/vector-im/element-android/releases

View File

@ -17,7 +17,7 @@ buildscript {
}
}
dependencies {
classpath "io.realm:realm-gradle-plugin:10.11.0"
classpath "io.realm:realm-gradle-plugin:10.11.1"
}
}
@ -60,7 +60,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.4.34\""
buildConfigField "String", "SDK_VERSION", "\"1.4.36\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
@ -163,6 +163,7 @@ dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor'
implementation libs.squareup.moshi
implementation libs.squareup.moshiAdapters
kapt libs.squareup.moshiKotlin
api "com.atlassian.commonmark:commonmark:0.13.0"
@ -199,7 +200,7 @@ dependencies {
implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.53'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.54'
testImplementation libs.tests.junit
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281

View File

@ -28,7 +28,7 @@ interface DebugService {
fun getAllRealmConfigurations(): List<RealmConfiguration>
/**
* Prints out info on DB size to logcat.
* Get info on DB size.
*/
fun logDbUsageInfo()
fun getDbUsageInfo(): String
}

View File

@ -89,10 +89,14 @@ fun Throwable.isInvalidUIAAuth() = this is Failure.ServerError &&
fun Throwable.isHomeserverUnavailable() = this is Failure.NetworkConnection &&
this.ioException is UnknownHostException
fun Throwable.isHomeserverConnectionError() = this is Failure.NetworkConnection
fun Throwable.isMissingEmailVerification() = this is Failure.ServerError &&
error.code == MatrixError.M_UNAUTHORIZED &&
error.message == "Unable to get validated threepid"
fun Throwable.isUnrecognisedCertificate() = this is Failure.UnrecognizedCertificateFailure
/**
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
*/

View File

@ -323,9 +323,9 @@ interface Session {
fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
/**
* Debug API, will print out info on DB size to logcat.
* Debug API, will return info about the DB.
*/
fun logDbUsageInfo()
fun getDbUsageInfo(): String
/**
* Debug API, return the list of all RealmConfiguration used by this session.

View File

@ -70,6 +70,9 @@ object EventType {
const val STATE_ROOM_ENCRYPTION = "m.room.encryption"
const val STATE_ROOM_SERVER_ACL = "m.room.server_acl"
// This type is for local purposes, it should never be processed by the server
const val LOCAL_STATE_ROOM_THIRD_PARTY_INVITE = "local.room.third_party_invite"
// Call Events
const val CALL_INVITE = "m.call.invite"
const val CALL_CANDIDATES = "m.call.candidates"

View File

@ -18,10 +18,14 @@ package org.matrix.android.sdk.api.session.identity
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier
sealed class ThreePid(open val value: String) {
@JsonClass(generateAdapter = true)
data class Email(val email: String) : ThreePid(email)
@JsonClass(generateAdapter = true)
data class Msisdn(val msisdn: String) : ThreePid(msisdn)
}

View File

@ -17,13 +17,16 @@
package org.matrix.android.sdk.api.session.room.model.create
import android.net.Uri
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.internal.di.MoshiProvider
@JsonClass(generateAdapter = true)
open class CreateRoomParams {
/**
* A public visibility indicates that the room will be shown in the published room list.
@ -61,12 +64,12 @@ open class CreateRoomParams {
* A list of user IDs to invite to the room.
* This will tell the server to invite everyone in the list to the newly created room.
*/
val invitedUserIds = mutableListOf<String>()
var invitedUserIds = mutableListOf<String>()
/**
* A list of objects representing third party IDs to invite into the room.
*/
val invite3pids = mutableListOf<ThreePid>()
var invite3pids = mutableListOf<ThreePid>()
/**
* Initial Guest Access.
@ -99,14 +102,14 @@ open class CreateRoomParams {
* The server will clobber the following keys: creator.
* Future versions of the specification may allow the server to clobber other keys.
*/
val creationContent = mutableMapOf<String, Any>()
var creationContent = mutableMapOf<String, Any>()
/**
* A list of state events to set in the new room. This allows the user to override the default state events
* set in the new room. The expected format of the state events are an object with type, state_key and content keys set.
* Takes precedence over events set by preset, but gets overridden by name and topic keys.
*/
val initialStates = mutableListOf<CreateRoomStateEvent>()
var initialStates = mutableListOf<CreateRoomStateEvent>()
/**
* Set to true to disable federation of this room.
@ -151,7 +154,7 @@ open class CreateRoomParams {
* Supported value: MXCRYPTO_ALGORITHM_MEGOLM.
*/
var algorithm: String? = null
private set
internal set
var historyVisibility: RoomHistoryVisibility? = null
@ -161,10 +164,18 @@ open class CreateRoomParams {
var roomVersion: String? = null
var featurePreset: RoomFeaturePreset? = null
@Transient var featurePreset: RoomFeaturePreset? = null
companion object {
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
private const val CREATION_CONTENT_KEY_ROOM_TYPE = "type"
internal const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
internal const val CREATION_CONTENT_KEY_ROOM_TYPE = "type"
fun fromJson(json: String?): CreateRoomParams? {
return json?.let { MoshiProvider.providesMoshi().adapter(CreateRoomParams::class.java).fromJson(it) }
}
}
}
internal fun CreateRoomParams.toJSONString(): String {
return MoshiProvider.providesMoshi().adapter(CreateRoomParams::class.java).toJson(this)
}

View File

@ -16,8 +16,10 @@
package org.matrix.android.sdk.api.session.room.model.create
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Content
@JsonClass(generateAdapter = true)
data class CreateRoomStateEvent(
/**
* Required. The type of event to send.

View File

@ -53,6 +53,11 @@ interface SyncService {
*/
fun getSyncState(): SyncState
/**
* This method returns true if the sync thread is alive, i.e. started.
*/
fun isSyncThreadAlive(): Boolean
/**
* This method allows to listen the sync state.
* @return a [LiveData] of [SyncState].

View File

@ -16,10 +16,12 @@
package org.matrix.android.sdk.internal.crypto.tasks
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.session.room.create.CreateRoomFromLocalRoomTask
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
import org.matrix.android.sdk.internal.task.Task
@ -37,12 +39,17 @@ internal class DefaultSendEventTask @Inject constructor(
private val localEchoRepository: LocalEchoRepository,
private val encryptEventTask: EncryptEventTask,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val createRoomFromLocalRoomTask: CreateRoomFromLocalRoomTask,
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver
) : SendEventTask {
override suspend fun execute(params: SendEventTask.Params): String {
try {
if (params.event.isLocalRoomEvent) {
return createRoomAndSendEvent(params)
}
// Make sure to load all members in the room before sending the event.
params.event.roomId
?.takeIf { params.encrypt }
@ -78,6 +85,12 @@ internal class DefaultSendEventTask @Inject constructor(
}
}
private suspend fun createRoomAndSendEvent(params: SendEventTask.Params): String {
val roomId = createRoomFromLocalRoomTask.execute(CreateRoomFromLocalRoomTask.Params(params.event.roomId.orEmpty()))
Timber.d("State event: convert local room (${params.event.roomId}) to existing room ($roomId) before sending the event.")
return execute(params.copy(event = params.event.copy(roomId = roomId)))
}
@Throws
private suspend fun handleEncryption(params: SendEventTask.Params): Event {
if (params.encrypt && !params.event.isEncrypted()) {
@ -91,4 +104,7 @@ internal class DefaultSendEventTask @Inject constructor(
}
return params.event
}
private val Event.isLocalRoomEvent
get() = RoomLocalEcho.isLocalEchoId(roomId.orEmpty())
}

View File

@ -76,12 +76,12 @@ internal class VerificationTransportToDevice(
.configureWith(SendToDeviceTask.Params(MessageType.MSGTYPE_VERIFICATION_REQUEST, contentMap)) {
this.callback = object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
Timber.v("## verification [$tx.transactionId] send toDevice request success")
Timber.v("## verification [${tx?.transactionId}] send toDevice request success")
callback.invoke(localId, validKeyReq)
}
override fun onFailure(failure: Throwable) {
Timber.e("## verification [$tx.transactionId] failed to send toDevice request")
Timber.e("## verification [${tx?.transactionId}] failed to send toDevice request")
}
}
}
@ -103,12 +103,12 @@ internal class VerificationTransportToDevice(
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_READY, contentMap)) {
this.callback = object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
Timber.v("## verification [$tx.transactionId] send toDevice request success")
Timber.v("## verification [${tx?.transactionId}] send toDevice request success")
callback?.invoke()
}
override fun onFailure(failure: Throwable) {
Timber.e("## verification [$tx.transactionId] failed to send toDevice request")
Timber.e("## verification [${tx?.transactionId}] failed to send toDevice request")
}
}
}
@ -136,7 +136,7 @@ internal class VerificationTransportToDevice(
.configureWith(SendToDeviceTask.Params(type, contentMap)) {
this.callback = object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
Timber.v("## SAS verification [$tx.transactionId] toDevice type '$type' success.")
Timber.v("## SAS verification [${tx.transactionId}] toDevice type '$type' success.")
if (onDone != null) {
onDone()
} else {
@ -149,7 +149,7 @@ internal class VerificationTransportToDevice(
}
override fun onFailure(failure: Throwable) {
Timber.e("## SAS verification [$tx.transactionId] failed to send toDevice in state : $tx.state")
Timber.e("## SAS verification [${tx.transactionId}] failed to send toDevice in state : ${tx.state}")
tx.cancel(onErrorReason)
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2022 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.database
import io.realm.DefaultCompactOnLaunchCallback
class RealmCompactOnLaunch : DefaultCompactOnLaunchCallback() {
/**
* Forces all RealmCompactOnLaunch instances to be equal.
* Avoids Realm throwing when multiple instances of this class are used.
*/
override fun equals(other: Any?) = other is RealmCompactOnLaunch
override fun hashCode() = 0x1000
}

View File

@ -38,7 +38,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
LiveEntityObserver, RealmChangeListener<RealmResults<T>> {
private companion object {
val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND")
val BACKGROUND_HANDLER = createBackgroundHandler("Matrix-LIVE_ENTITY_BACKGROUND")
}
protected val observerScope = CoroutineScope(SupervisorJob() + BACKGROUND_HANDLER.asCoroutineDispatcher())

View File

@ -52,6 +52,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo032
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo033
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject
@ -60,7 +61,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer
) : MatrixRealmMigration(
dbName = "Session",
schemaVersion = 35L,
schemaVersion = 36L,
) {
/**
* Forces all RealmSessionStoreMigration instances to be equal.
@ -105,5 +106,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 33) MigrateSessionTo033(realm).perform()
if (oldVersion < 34) MigrateSessionTo034(realm).perform()
if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform()
}
}

View File

@ -64,7 +64,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
}
val realmConfiguration = RealmConfiguration.Builder()
.compactOnLaunch()
.compactOnLaunch(RealmCompactOnLaunch())
.directory(directory)
.name(REALM_NAME)
.apply {
@ -93,7 +93,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
return
}
listOf(REALM_NAME, "$REALM_NAME.lock", "$REALM_NAME.note", "$REALM_NAME.management").forEach { file ->
listOf(REALM_NAME, "${REALM_NAME}.lock", "${REALM_NAME}.note", "${REALM_NAME}.management").forEach { file ->
try {
File(directory, file).deleteRecursively()
} catch (e: Exception) {

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 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.database.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
internal class MigrateSessionTo036(realm: DynamicRealm) : RealmMigrator(realm, 36) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.create("LocalRoomSummaryEntity")
.addField(LocalRoomSummaryEntityFields.ROOM_ID, String::class.java)
.addPrimaryKey(LocalRoomSummaryEntityFields.ROOM_ID)
.setRequired(LocalRoomSummaryEntityFields.ROOM_ID, true)
.addField(LocalRoomSummaryEntityFields.CREATE_ROOM_PARAMS_STR, String::class.java)
.addRealmObjectField(LocalRoomSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2022 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.database.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.toJSONString
internal open class LocalRoomSummaryEntity(
@PrimaryKey var roomId: String = "",
var roomSummaryEntity: RoomSummaryEntity? = null,
private var createRoomParamsStr: String? = null
) : RealmObject() {
var createRoomParams: CreateRoomParams?
get() {
return CreateRoomParams.fromJson(createRoomParamsStr)
}
set(value) {
createRoomParamsStr = value?.toJSONString()
}
companion object
}

View File

@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
ReadReceiptEntity::class,
RoomEntity::class,
RoomSummaryEntity::class,
LocalRoomSummaryEntity::class,
RoomTagEntity::class,
SyncEntity::class,
PendingThreePidEntity::class,

View File

@ -0,0 +1,31 @@
/*
* Copyright 2022 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.database.query
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
internal fun LocalRoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<LocalRoomSummaryEntity> {
val query = realm.where<LocalRoomSummaryEntity>()
if (roomId != null) {
query.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId)
}
return query
}

View File

@ -19,16 +19,15 @@ package org.matrix.android.sdk.internal.database.tools
import io.realm.Realm
import io.realm.RealmConfiguration
import org.matrix.android.sdk.BuildConfig
import timber.log.Timber
internal class RealmDebugTools(
private val realmConfiguration: RealmConfiguration
) {
/**
* Log info about the DB.
* Get info about the DB.
*/
fun logInfo(baseName: String) {
buildString {
fun getInfo(baseName: String): String {
return buildString {
append("\n$baseName Realm located at : ${realmConfiguration.realmDirectory}/${realmConfiguration.realmFileName}")
if (BuildConfig.LOG_PRIVATE_DATA) {
@ -54,7 +53,6 @@ internal class RealmDebugTools(
separator()
}
}
.let { Timber.i(it) }
}
private fun StringBuilder.separator() = append("\n==============================================")

View File

@ -36,9 +36,9 @@ internal class DefaultDebugService @Inject constructor(
realmConfigurationGlobal
}
override fun logDbUsageInfo() {
RealmDebugTools(realmConfigurationAuth).logInfo("Auth")
RealmDebugTools(realmConfigurationGlobal).logInfo("Global")
sessionManager.getLastSession()?.logDbUsageInfo()
override fun getDbUsageInfo() = buildString {
append(RealmDebugTools(realmConfigurationAuth).getInfo("Auth"))
append(RealmDebugTools(realmConfigurationGlobal).getInfo("Global"))
append(sessionManager.getLastSession()?.getDbUsageInfo())
}
}

View File

@ -40,7 +40,7 @@ internal object MatrixModule {
io = Dispatchers.IO,
computation = Dispatchers.Default,
main = Dispatchers.Main,
crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher(),
crypto = createBackgroundHandler("Matrix-Crypto_Thread").asCoroutineDispatcher(),
dmVerif = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
)
}

View File

@ -17,6 +17,8 @@
package org.matrix.android.sdk.internal.di
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageDefaultContent
@ -60,6 +62,12 @@ internal object MoshiProvider {
.registerSubtype(MessagePollResponseContent::class.java, MessageType.MSGTYPE_POLL_RESPONSE)
)
.add(SerializeNulls.JSON_ADAPTER_FACTORY)
.add(
PolymorphicJsonAdapterFactory.of(ThreePid::class.java, "type")
.withSubtype(ThreePid.Email::class.java, "email")
.withSubtype(ThreePid.Msisdn::class.java, "msisdn")
.withDefaultValue(null)
)
.build()
fun providesMoshi(): Moshi {

View File

@ -263,11 +263,11 @@ internal class DefaultSession @Inject constructor(
}
}
override fun logDbUsageInfo() {
RealmDebugTools(realmConfiguration).logInfo("Session")
RealmDebugTools(realmConfigurationCrypto).logInfo("Crypto")
RealmDebugTools(realmConfigurationIdentity).logInfo("Identity")
RealmDebugTools(realmConfigurationContentScanner).logInfo("ContentScanner")
override fun getDbUsageInfo() = buildString {
append(RealmDebugTools(realmConfiguration).getInfo("Session"))
append(RealmDebugTools(realmConfigurationCrypto).getInfo("Crypto"))
append(RealmDebugTools(realmConfigurationIdentity).getInfo("Identity"))
append(RealmDebugTools(realmConfigurationContentScanner).getInfo("ContentScanner"))
}
override fun getRealmConfigurations(): List<RealmConfiguration> {

View File

@ -43,9 +43,13 @@ import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomLocalAli
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.GetRoomLocalAliasesTask
import org.matrix.android.sdk.internal.session.room.create.CreateLocalRoomStateEventsTask
import org.matrix.android.sdk.internal.session.room.create.CreateLocalRoomTask
import org.matrix.android.sdk.internal.session.room.create.CreateRoomFromLocalRoomTask
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
import org.matrix.android.sdk.internal.session.room.create.DefaultCreateLocalRoomStateEventsTask
import org.matrix.android.sdk.internal.session.room.create.DefaultCreateLocalRoomTask
import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomFromLocalRoomTask
import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomTask
import org.matrix.android.sdk.internal.session.room.delete.DefaultDeleteLocalRoomTask
import org.matrix.android.sdk.internal.session.room.delete.DeleteLocalRoomTask
@ -213,6 +217,12 @@ internal abstract class RoomModule {
@Binds
abstract fun bindCreateLocalRoomTask(task: DefaultCreateLocalRoomTask): CreateLocalRoomTask
@Binds
abstract fun bindCreateLocalRoomStateEventsTask(task: DefaultCreateLocalRoomStateEventsTask): CreateLocalRoomStateEventsTask
@Binds
abstract fun bindCreateRoomFromLocalRoomTask(task: DefaultCreateRoomFromLocalRoomTask): CreateRoomFromLocalRoomTask
@Binds
abstract fun bindDeleteLocalRoomTask(task: DefaultDeleteLocalRoomTask): DeleteLocalRoomTask

View File

@ -0,0 +1,299 @@
/*
* Copyright 2022 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.room.create
import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.RoomNameContent
import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
import org.matrix.android.sdk.api.session.room.model.banOrDefault
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.model.eventsDefaultOrDefault
import org.matrix.android.sdk.api.session.room.model.inviteOrDefault
import org.matrix.android.sdk.api.session.room.model.kickOrDefault
import org.matrix.android.sdk.api.session.room.model.redactOrDefault
import org.matrix.android.sdk.api.session.room.model.stateDefaultOrDefault
import org.matrix.android.sdk.api.session.room.model.usersDefaultOrDefault
import org.matrix.android.sdk.api.session.user.UserService
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.create.CreateLocalRoomStateEventsTask.Params
import org.matrix.android.sdk.internal.session.room.membership.threepid.toThreePid
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
/**
* Generate a list of local state events from the given [CreateRoomBody].
* The states events are generated according to the given configuration and following the matrix specification.
* This list reflects as much as possible a list of state events related to a real room configured and got from the server.
*
* Ref: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3createroom
*/
internal interface CreateLocalRoomStateEventsTask : Task<Params, List<Event>> {
data class Params(val createRoomBody: CreateRoomBody)
}
internal class DefaultCreateLocalRoomStateEventsTask @Inject constructor(
@UserId private val myUserId: String,
private val userService: UserService,
private val clock: Clock,
) : CreateLocalRoomStateEventsTask {
private lateinit var createRoomBody: CreateRoomBody
override suspend fun execute(params: Params): List<Event> {
createRoomBody = params.createRoomBody
// Build the list of the state events following the priorities from the matrix specification
// Changing the order of the events might break the correct display of the room on the client side
return buildList {
createRoomCreateEvent()
createRoomMemberEvents(listOf(myUserId))
createRoomPowerLevelsEvent()
createRoomAliasEvent()
createRoomPresetEvents()
createRoomInitialStateEvents()
createRoomNameAndTopicStateEvents()
createRoomMemberEvents(createRoomBody.invitedUserIds.orEmpty())
createRoomThreePidEvents()
createRoomDefaultEvents()
}
}
/**
* Generate the create state event related to this room.
*/
private fun MutableList<Event>.createRoomCreateEvent() {
val roomCreateEvent = createLocalStateEvent(
type = EventType.STATE_ROOM_CREATE,
content = RoomCreateContent(
creator = myUserId,
roomVersion = createRoomBody.roomVersion,
type = (createRoomBody.creationContent as? Map<*, *>)?.get(CreateRoomParams.CREATION_CONTENT_KEY_ROOM_TYPE) as? String
).toContent(),
)
add(roomCreateEvent)
}
/**
* Generate the create state event related to the power levels using the given overridden values or the default values according to the specification.
* Ref: https://spec.matrix.org/latest/client-server-api/#mroompower_levels
*/
private fun MutableList<Event>.createRoomPowerLevelsEvent() {
val powerLevelsContent = createLocalStateEvent(
type = EventType.STATE_ROOM_POWER_LEVELS,
content = (createRoomBody.powerLevelContentOverride ?: PowerLevelsContent()).let {
it.copy(
ban = it.banOrDefault(),
eventsDefault = it.eventsDefaultOrDefault(),
invite = it.inviteOrDefault(),
kick = it.kickOrDefault(),
redact = it.redactOrDefault(),
stateDefault = it.stateDefaultOrDefault(),
usersDefault = it.usersDefaultOrDefault(),
)
}.toContent(),
)
add(powerLevelsContent)
}
/**
* Generate the local room member state events related to the given user ids, if any.
*/
private suspend fun MutableList<Event>.createRoomMemberEvents(userIds: List<String>) {
val memberEvents = userIds
.mapNotNull { tryOrNull { userService.resolveUser(it) } }
.map { user ->
createLocalStateEvent(
type = EventType.STATE_ROOM_MEMBER,
content = RoomMemberContent(
isDirect = createRoomBody.isDirect.takeUnless { user.userId == myUserId }.orFalse(),
membership = if (user.userId == myUserId) Membership.JOIN else Membership.INVITE,
displayName = user.displayName,
avatarUrl = user.avatarUrl
).toContent(),
stateKey = user.userId
)
}
addAll(memberEvents)
}
/**
* Generate the local state events related to the given third party invites, if any.
*/
private fun MutableList<Event>.createRoomThreePidEvents() {
createRoomBody.invite3pids.orEmpty().forEach { body ->
val localThirdPartyInviteEvent = createLocalStateEvent(
type = EventType.LOCAL_STATE_ROOM_THIRD_PARTY_INVITE,
content = LocalRoomThirdPartyInviteContent(
isDirect = createRoomBody.isDirect.orFalse(),
membership = Membership.INVITE,
displayName = body.address,
thirdPartyInvite = body.toThreePid()
).toContent(),
)
val thirdPartyInviteEvent = createLocalStateEvent(
type = EventType.STATE_ROOM_THIRD_PARTY_INVITE,
content = RoomThirdPartyInviteContent(
displayName = body.address,
keyValidityUrl = null,
publicKey = null,
publicKeys = null
).toContent(),
)
add(localThirdPartyInviteEvent)
add(thirdPartyInviteEvent)
}
}
/**
* Generate the local state event related to the given alias, if any.
*/
fun MutableList<Event>.createRoomAliasEvent() {
if (createRoomBody.roomAliasName != null) {
val canonicalAliasContent = createLocalStateEvent(
type = EventType.STATE_ROOM_CANONICAL_ALIAS,
content = RoomCanonicalAliasContent(
canonicalAlias = "${createRoomBody.roomAliasName}:${myUserId.getServerName()}"
).toContent(),
)
add(canonicalAliasContent)
}
}
/**
* Generate the local state events related to the given [CreateRoomPreset].
* Ref: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3createroom
*/
private fun MutableList<Event>.createRoomPresetEvents() {
val preset = createRoomBody.preset ?: return
var joinRules: RoomJoinRules? = null
var historyVisibility: RoomHistoryVisibility? = null
var guestAccess: GuestAccess? = null
when (preset) {
CreateRoomPreset.PRESET_PRIVATE_CHAT,
CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT -> {
joinRules = RoomJoinRules.INVITE
historyVisibility = RoomHistoryVisibility.SHARED
guestAccess = GuestAccess.CanJoin
}
CreateRoomPreset.PRESET_PUBLIC_CHAT -> {
joinRules = RoomJoinRules.PUBLIC
historyVisibility = RoomHistoryVisibility.SHARED
guestAccess = GuestAccess.Forbidden
}
}
add(createLocalStateEvent(EventType.STATE_ROOM_JOIN_RULES, RoomJoinRulesContent(joinRules.value).toContent()))
add(createLocalStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, RoomHistoryVisibilityContent(historyVisibility.value).toContent()))
add(createLocalStateEvent(EventType.STATE_ROOM_GUEST_ACCESS, RoomGuestAccessContent(guestAccess.value).toContent()))
}
/**
* Generate the local state events related to the given initial states, if any.
* The given initial state events override the potential existing ones of the same type.
*/
private fun MutableList<Event>.createRoomInitialStateEvents() {
val initialStates = createRoomBody.initialStates ?: return
val initialStateEvents = initialStates.map { createLocalStateEvent(it.type, it.content, it.stateKey) }
// Erase existing events of the same type
removeAll { event -> event.type in initialStateEvents.map { it.type } }
// Add the initial state events to the list
addAll(initialStateEvents)
}
/**
* Generate the local events related to the given room name and topic, if any.
*/
private fun MutableList<Event>.createRoomNameAndTopicStateEvents() {
if (createRoomBody.name != null) {
add(createLocalStateEvent(EventType.STATE_ROOM_NAME, RoomNameContent(createRoomBody.name).toContent()))
}
if (createRoomBody.topic != null) {
add(createLocalStateEvent(EventType.STATE_ROOM_TOPIC, RoomTopicContent(createRoomBody.topic).toContent()))
}
}
/**
* Generate the local events which have not been set and are in that case provided by the server with default values.
* Default events:
* - m.room.history_visibility (https://spec.matrix.org/latest/client-server-api/#server-behaviour-5)
* - m.room.guest_access (https://spec.matrix.org/latest/client-server-api/#mroomguest_access)
*/
private fun MutableList<Event>.createRoomDefaultEvents() {
// HistoryVisibility
if (none { it.type == EventType.STATE_ROOM_HISTORY_VISIBILITY }) {
add(
createLocalStateEvent(
type = EventType.STATE_ROOM_HISTORY_VISIBILITY,
content = RoomHistoryVisibilityContent(RoomHistoryVisibility.SHARED.value).toContent(),
)
)
}
// GuestAccess
if (none { it.type == EventType.STATE_ROOM_GUEST_ACCESS }) {
add(
createLocalStateEvent(
type = EventType.STATE_ROOM_GUEST_ACCESS,
content = RoomGuestAccessContent(GuestAccess.Forbidden.value).toContent(),
)
)
}
}
/**
* Generate a local state event from the given parameters.
*
* @param type the event type, see [EventType]
* @param content the content of the event
* @param stateKey the stateKey, if any
*
* @return a local state event
*/
private fun createLocalStateEvent(type: String?, content: Content?, stateKey: String? = ""): Event {
return Event(
type = type,
senderId = myUserId,
stateKey = stateKey,
content = content,
originServerTs = clock.epochMillis(),
eventId = LocalEcho.createLocalEchoId()
)
}
}

View File

@ -21,26 +21,15 @@ import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.createObject
import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary
import org.matrix.android.sdk.api.session.user.UserService
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
import org.matrix.android.sdk.internal.database.mapper.asDomain
@ -48,6 +37,7 @@ import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
@ -56,7 +46,6 @@ import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.getOrNull
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberEventHandler
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
@ -70,22 +59,22 @@ import javax.inject.Inject
internal interface CreateLocalRoomTask : Task<CreateRoomParams, String>
internal class DefaultCreateLocalRoomTask @Inject constructor(
@UserId private val userId: String,
@SessionDatabase private val monarchy: Monarchy,
private val roomMemberEventHandler: RoomMemberEventHandler,
private val roomSummaryUpdater: RoomSummaryUpdater,
@SessionDatabase private val realmConfiguration: RealmConfiguration,
private val createRoomBodyBuilder: CreateRoomBodyBuilder,
private val userService: UserService,
private val cryptoService: DefaultCryptoService,
private val clock: Clock,
private val createLocalRoomStateEventsTask: CreateLocalRoomStateEventsTask,
) : CreateLocalRoomTask {
override suspend fun execute(params: CreateRoomParams): String {
val createRoomBody = createRoomBodyBuilder.build(params.withDefault())
val createRoomBody = createRoomBodyBuilder.build(params)
val roomId = RoomLocalEcho.createLocalEchoId()
monarchy.awaitTransaction { realm ->
createLocalRoomEntity(realm, roomId, createRoomBody)
createLocalRoomSummaryEntity(realm, roomId, createRoomBody)
createLocalRoomSummaryEntity(realm, roomId, params, createRoomBody)
}
// Wait for room to be created in DB
@ -114,14 +103,29 @@ internal class DefaultCreateLocalRoomTask @Inject constructor(
}
}
private fun createLocalRoomSummaryEntity(realm: Realm, roomId: String, createRoomBody: CreateRoomBody) {
val otherUserId = createRoomBody.getDirectUserId()
if (otherUserId != null) {
RoomSummaryEntity.getOrCreate(realm, roomId).apply {
private fun createLocalRoomSummaryEntity(realm: Realm, roomId: String, createRoomParams: CreateRoomParams, createRoomBody: CreateRoomBody) {
// Create the room summary entity
val roomSummaryEntity = realm.createObject<RoomSummaryEntity>(roomId).apply {
val otherUserId = createRoomBody.getDirectUserId()
if (otherUserId != null) {
isDirect = true
directUserId = otherUserId
}
}
// Update the createRoomParams from the potential feature preset before saving
createRoomParams.featurePreset?.let { featurePreset ->
featurePreset.updateRoomParams(createRoomParams)
createRoomParams.initialStates.addAll(featurePreset.setupInitialStates().orEmpty())
}
// Create a LocalRoomSummaryEntity decorated by the related RoomSummaryEntity and the updated CreateRoomParams
realm.createObject<LocalRoomSummaryEntity>(roomId).also {
it.roomSummaryEntity = roomSummaryEntity
it.createRoomParams = createRoomParams
}
// Update the RoomSummaryEntity by simulating a fake sync response
roomSummaryUpdater.update(
realm = realm,
roomId = roomId,
@ -150,7 +154,7 @@ internal class DefaultCreateLocalRoomTask @Inject constructor(
isLastForward = true
}
val eventList = createLocalRoomEvents(createRoomBody)
val eventList = createLocalRoomStateEventsTask.execute(CreateLocalRoomStateEventsTask.Params(createRoomBody))
val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>()
for (event in eventList) {
@ -169,6 +173,9 @@ internal class DefaultCreateLocalRoomTask @Inject constructor(
roomMemberContentsByUser[event.stateKey] = event.getFixedRoomMemberContent()
roomMemberEventHandler.handle(realm, roomId, event, false)
}
// Give info to crypto module
cryptoService.onStateEvent(roomId, event)
}
roomMemberContentsByUser.getOrPut(event.senderId) {
@ -187,81 +194,4 @@ internal class DefaultCreateLocalRoomTask @Inject constructor(
return chunkEntity
}
/**
* Build the list of the events related to the room creation params.
*
* @param createRoomBody the room creation params
*
* @return the list of events
*/
private suspend fun createLocalRoomEvents(createRoomBody: CreateRoomBody): List<Event> {
val myUser = userService.getUser(userId) ?: User(userId)
val invitedUsers = createRoomBody.invitedUserIds.orEmpty()
.mapNotNull { tryOrNull { userService.resolveUser(it) } }
val createRoomEvent = createLocalEvent(
type = EventType.STATE_ROOM_CREATE,
content = RoomCreateContent(
creator = userId
).toContent()
)
val myRoomMemberEvent = createLocalEvent(
type = EventType.STATE_ROOM_MEMBER,
content = RoomMemberContent(
membership = Membership.JOIN,
displayName = myUser.displayName,
avatarUrl = myUser.avatarUrl
).toContent(),
stateKey = userId
)
val roomMemberEvents = invitedUsers.map {
createLocalEvent(
type = EventType.STATE_ROOM_MEMBER,
content = RoomMemberContent(
isDirect = createRoomBody.isDirect.orFalse(),
membership = Membership.INVITE,
displayName = it.displayName,
avatarUrl = it.avatarUrl
).toContent(),
stateKey = it.userId
)
}
return buildList {
add(createRoomEvent)
add(myRoomMemberEvent)
addAll(createRoomBody.initialStates.orEmpty().map { createLocalEvent(it.type, it.content, it.stateKey) })
addAll(roomMemberEvents)
}
}
/**
* Generate a local event from the given parameters.
*
* @param type the event type, see [EventType]
* @param content the content of the Event
* @param stateKey the stateKey, if any
*
* @return a fake event
*/
private fun createLocalEvent(type: String?, content: Content?, stateKey: String? = ""): Event {
return Event(
type = type,
senderId = userId,
stateKey = stateKey,
content = content,
originServerTs = clock.epochMillis(),
eventId = LocalEcho.createLocalEchoId()
)
}
/**
* Setup default values to the CreateRoomParams as the room is created locally (the default values will not be defined by the server).
*/
private fun CreateRoomParams.withDefault() = this.apply {
if (visibility == null) visibility = RoomDirectoryVisibility.PRIVATE
if (historyVisibility == null) historyVisibility = RoomHistoryVisibility.SHARED
if (guestAccess == null) guestAccess = GuestAccess.Forbidden
}
}

View File

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody
/**
@ -119,7 +120,13 @@ internal data class CreateRoomBody(
*/
@Json(name = "room_version")
val roomVersion: String?
)
) {
companion object {
fun fromJson(json: String?): CreateRoomBody? {
return json?.let { MoshiProvider.providesMoshi().adapter(CreateRoomBody::class.java).fromJson(it) }
}
}
}
/**
* Tells if the created room can be a direct chat one.

View File

@ -0,0 +1,149 @@
/*
* Copyright 2022 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.room.create
import com.zhuinden.monarchy.Monarchy
import io.realm.kotlin.where
import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.extensions.orFalse
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.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Create a room on the server from a local room.
* The configuration of the local room will be use to configure the new room.
* The potential local room members will also be invited to this new room.
*
* A local tombstone event will be created to indicate that the local room has been replacing by the new one.
*/
internal interface CreateRoomFromLocalRoomTask : Task<CreateRoomFromLocalRoomTask.Params, String> {
data class Params(val localRoomId: String)
}
internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor(
@UserId private val userId: String,
@SessionDatabase private val monarchy: Monarchy,
private val createRoomTask: CreateRoomTask,
private val stateEventDataSource: StateEventDataSource,
private val clock: Clock,
) : CreateRoomFromLocalRoomTask {
private val realmConfiguration
get() = monarchy.realmConfiguration
override suspend fun execute(params: CreateRoomFromLocalRoomTask.Params): String {
val replacementRoomId = stateEventDataSource.getStateEvent(params.localRoomId, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)
?.content.toModel<RoomTombstoneContent>()
?.replacementRoomId
if (replacementRoomId != null) {
return replacementRoomId
}
var createRoomParams: CreateRoomParams? = null
var isEncrypted = false
monarchy.doWithRealm { realm ->
realm.where<LocalRoomSummaryEntity>()
.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, params.localRoomId)
.findFirst()
?.let {
createRoomParams = it.createRoomParams
isEncrypted = it.roomSummaryEntity?.isEncrypted.orFalse()
}
}
val roomId = createRoomTask.execute(createRoomParams!!)
try {
// Wait for all the room events before triggering the replacement room
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(RoomSummaryEntity::class.java)
.equalTo(RoomSummaryEntityFields.ROOM_ID, roomId)
.equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, createRoomParams?.invitedUserIds?.size ?: 0)
}
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
EventEntity.whereRoomId(realm, roomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY)
}
if (isEncrypted) {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
EventEntity.whereRoomId(realm, roomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION)
}
}
} catch (exception: TimeoutCancellationException) {
throw CreateRoomFailure.CreatedWithTimeout(roomId)
}
createTombstoneEvent(params, roomId)
return roomId
}
/**
* Create a Tombstone event to indicate that the local room has been replaced by a new one.
*/
private suspend fun createTombstoneEvent(params: CreateRoomFromLocalRoomTask.Params, roomId: String) {
val now = clock.epochMillis()
val event = Event(
type = EventType.STATE_ROOM_TOMBSTONE,
senderId = userId,
originServerTs = now,
stateKey = "",
eventId = UUID.randomUUID().toString(),
content = RoomTombstoneContent(
replacementRoomId = roomId
).toContent()
)
monarchy.awaitTransaction { realm ->
val eventEntity = event.toEntity(params.localRoomId, SendState.SYNCED, now).copyToRealmOrIgnore(realm, EventInsertType.INCREMENTAL_SYNC)
if (event.stateKey != null && event.type != null && event.eventId != null) {
CurrentStateEventEntity.getOrCreate(realm, params.localRoomId, event.stateKey, event.type).apply {
eventId = event.eventId
root = eventEntity
}
}
}
}
}

View File

@ -54,8 +54,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
private val directChatsHelper: DirectChatsHelper,
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val readMarkersTask: SetReadMarkersTask,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
@SessionDatabase private val realmConfiguration: RealmConfiguration,
private val createRoomBodyBuilder: CreateRoomBodyBuilder,
private val globalErrorReceiver: GlobalErrorReceiver,
private val clock: Clock,
@ -71,7 +70,6 @@ internal class DefaultCreateRoomTask @Inject constructor(
}
val createRoomBody = createRoomBodyBuilder.build(params)
val createRoomResponse = try {
executeRequest(globalErrorReceiver) {
roomAPI.createRoom(createRoomBody)
@ -90,6 +88,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
}
throw throwable
}
val roomId = createRoomResponse.roomId
// Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before)
try {

View File

@ -0,0 +1,34 @@
/*
* Copyright 2022 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.room.create
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.room.model.Membership
/**
* Class representing the EventType.LOCAL_STATE_ROOM_THIRD_PARTY_INVITE state event content
* This class is only used to store the third party invite data of a local room.
*/
@JsonClass(generateAdapter = true)
internal data class LocalRoomThirdPartyInviteContent(
@Json(name = "membership") val membership: Membership,
@Json(name = "displayname") val displayName: String? = null,
@Json(name = "is_direct") val isDirect: Boolean = false,
@Json(name = "third_party_invite") val thirdPartyInvite: ThreePid? = null,
)

View File

@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
@ -70,6 +71,9 @@ internal class DefaultDeleteLocalRoomTask @Inject constructor(
RoomEntity.where(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - RoomEntity - delete ${it.size} entries") }
?.deleteAllFromRealm()
LocalRoomSummaryEntity.where(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - LocalRoomSummaryEntity - delete ${it.size} entries") }
?.deleteAllFromRealm()
}
} else {
Timber.i("## DeleteLocalRoomTask - Failed to remove room with id $roomId: not a local room")

View File

@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.session.room.membership.threepid
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.internal.auth.data.ThreePidMedium
@JsonClass(generateAdapter = true)
internal data class ThreePidInviteBody(
@ -43,3 +45,9 @@ internal data class ThreePidInviteBody(
@Json(name = "address")
val address: String
)
internal fun ThreePidInviteBody.toThreePid() = when (medium) {
ThreePidMedium.EMAIL -> ThreePid.Email(address)
ThreePidMedium.MSISDN -> ThreePid.Msisdn(address)
else -> null
}

View File

@ -53,7 +53,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
@Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
@Inject lateinit var localEchoRepository: LocalEchoRepository
override fun doOnError(params: Params): Result {
override fun doOnError(params: Params, failureMessage: String): Result {
params.localEchoIds.forEach { localEchoIds ->
localEchoRepository.updateSendState(
eventId = localEchoIds.eventId,
@ -63,7 +63,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
)
}
return super.doOnError(params)
return super.doOnError(params, failureMessage)
}
override fun injectWith(injector: SessionComponent) {

View File

@ -55,7 +55,7 @@ internal class EventSenderProcessorThread @Inject constructor(
private val queuedTaskFactory: QueuedTaskFactory,
private val taskExecutor: TaskExecutor,
private val memento: QueueMemento
) : Thread("SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor {
) : Thread("Matrix-SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor {
private fun markAsManaged(task: QueuedTask) {
memento.track(task)

View File

@ -16,10 +16,12 @@
package org.matrix.android.sdk.internal.session.room.state
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.session.room.create.CreateRoomFromLocalRoomTask
import org.matrix.android.sdk.internal.task.Task
import timber.log.Timber
import javax.inject.Inject
@ -35,28 +37,40 @@ internal interface SendStateTask : Task<SendStateTask.Params, String> {
internal class DefaultSendStateTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver
private val globalErrorReceiver: GlobalErrorReceiver,
private val createRoomFromLocalRoomTask: CreateRoomFromLocalRoomTask,
) : SendStateTask {
override suspend fun execute(params: SendStateTask.Params): String {
return executeRequest(globalErrorReceiver) {
val response = if (params.stateKey.isEmpty()) {
roomAPI.sendStateEvent(
roomId = params.roomId,
stateEventType = params.eventType,
params = params.body
)
if (RoomLocalEcho.isLocalEchoId(params.roomId)) {
// Room is local, so create a real one and send the event to this new room
createRoomAndSendEvent(params)
} else {
roomAPI.sendStateEvent(
roomId = params.roomId,
stateEventType = params.eventType,
stateKey = params.stateKey,
params = params.body
)
}
response.eventId.also {
Timber.d("State event: $it just sent in room ${params.roomId}")
val response = if (params.stateKey.isEmpty()) {
roomAPI.sendStateEvent(
roomId = params.roomId,
stateEventType = params.eventType,
params = params.body
)
} else {
roomAPI.sendStateEvent(
roomId = params.roomId,
stateEventType = params.eventType,
stateKey = params.stateKey,
params = params.body
)
}
response.eventId.also {
Timber.d("State event: $it just sent in room ${params.roomId}")
}
}
}
}
private suspend fun createRoomAndSendEvent(params: SendStateTask.Params): String {
val roomId = createRoomFromLocalRoomTask.execute(CreateRoomFromLocalRoomTask.Params(params.roomId))
Timber.d("State event: convert local room (${params.roomId}) to existing room ($roomId) before sending the event.")
return execute(params.copy(roomId = roomId))
}
}

View File

@ -76,7 +76,7 @@ internal class DefaultTimeline(
) : Timeline {
companion object {
val BACKGROUND_HANDLER = createBackgroundHandler("DefaultTimeline_Thread")
val BACKGROUND_HANDLER = createBackgroundHandler("Matrix-DefaultTimeline_Thread")
}
override val timelineID = UUID.randomUUID().toString()

View File

@ -73,6 +73,8 @@ internal class DefaultSyncService @Inject constructor(
override fun getSyncState() = getSyncThread().currentState()
override fun isSyncThreadAlive() = getSyncThread().isAlive
override fun getSyncRequestStateFlow() = syncRequestStateTracker.syncRequestState
override fun hasAlreadySynced(): Boolean {

View File

@ -62,7 +62,7 @@ internal class SyncThread @Inject constructor(
private val backgroundDetectionObserver: BackgroundDetectionObserver,
private val activeCallHandler: ActiveCallHandler,
private val lightweightSettingsStorage: DefaultLightweightSettingsStorage
) : Thread("SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
) : Thread("Matrix-SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
private var state: SyncState = SyncState.Idle
private var liveState = MutableLiveData(state)

View File

@ -30,6 +30,7 @@ import org.matrix.android.sdk.internal.session.sync.SyncTask
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.startChain
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -136,6 +137,7 @@ internal class SyncWorker(context: Context, workerParameters: WorkerParameters,
.setConstraints(WorkManagerProvider.workConstraints)
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.setInputData(data)
.startChain(true)
.build()
workManagerProvider.workManager
.enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)

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