[issue-2610] Merge branch 'develop' of github.com:mitchnull/element-android into feature/issue-2610-override-nick-color-via-user-account-data

This commit is contained in:
Péter Radics 2021-05-19 10:41:41 +02:00
commit 79a3be3ffe
346 changed files with 5294 additions and 1362 deletions

View File

@ -7,15 +7,6 @@
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />

View File

@ -1,4 +1,4 @@
# FTR: Configuration on https://travis-ci.org/vector-im/riotX-android/settings
# FTR: Configuration on https://travis-ci.org/github/vector-im/element-android/settings
#
# - Build only if .travis.yml is present -> On
# - Limit concurrent jobs -> Off
@ -8,53 +8,11 @@
# - Auto cancel branch builds -> On
# - Auto cancel pull request builds -> On
language: android
jdk: oraclejdk8
sudo: false
notifications:
email: false
android:
components:
# Uncomment the lines below if you want to
# use the latest revision of Android SDK Tools
- tools
- platform-tools
# The BuildTools version used by your project
- build-tools-29.0.3
# The SDK version used to compile your project
- android-29
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
# Build with the development SDK
before_script:
# Not necessary for the moment
# - /bin/sh ./set_debug_env.sh
# Just build the project for now
# Just run a simple script here
script:
# Build app (assembleGplayRelease assembleFdroidRelease)
# Build Android test (assembleAndroidTest) (disabled for now)
# Code quality (lintGplayRelease lintFdroidRelease)
# Split into two steps because if a task contain Fdroid, PlayService will be disabled
# Done by Buildkite now: - ./gradlew clean assembleGplayRelease lintGplayRelease --stacktrace
# Done by Buildkite now: - ./gradlew clean assembleFdroidRelease lintFdroidRelease --stacktrace
# Run unitary test (Disable for now, see https://travis-ci.org/vector-im/riot-android/builds/502504370)
# - ./gradlew testGplayReleaseUnitTest --stacktrace
# Other code quality check
# Done by Buildkite now: - ./tools/check/check_code_quality.sh
- ./tools/travis/check_pr.sh
# Check that indonesians file are identical. Due to Android issue, the resource folder must be value-in/, and Weblate export data into value-id/.
# Done by Buildkite now: - diff ./vector/src/main/res/values-id/strings.xml ./vector/src/main/res/values-in/strings.xml

View File

@ -1,4 +1,35 @@
Changes in Element 1.1.7 (2021-XX-XX)
Changes in Element 1.1.8 (2021-XX-XX)
===================================================
Features ✨:
-
Improvements 🙌:
-
Bugfix 🐛:
- Space Invite by link not always displayed for public space (#3345)
- Wrong copy in share space bottom sheet (#3346)
- Fix a problem with database migration on nightly builds (#3335)
Translations 🗣:
-
SDK API changes ⚠️:
-
Build 🧱:
- Compile with Kotlin 1.5.
- Upgrade some dependencies: gradle wrapper, third party lib, etc.
- Sign APK with build tools 30.0.3
Test:
-
Other changes:
- Add documentation on LoginWizard and RegistrationWizard (#3303)
Changes in Element 1.1.7 (2021-05-12)
===================================================
Features ✨:
@ -12,6 +43,7 @@ Improvements 🙌:
- Improve file too big error detection (#3245)
- User can now select video when selecting Gallery to send attachments to a room
- Add option to record a video from the camera
- Add the public icon on the rooms in the room list (#3292)
Bugfix 🐛:
- Message states cosmetic changes (#3007)
@ -25,9 +57,8 @@ Bugfix 🐛:
- Fix read marker not updating automatically (#3267)
- Sent video does not contains duration (#3272)
- Properly clean the back stack if the user cancel registration when waiting for email validation
Translations 🗣:
-
- Fix read marker visibility/position when filtering some events
- Fix user invitation in case of restricted profile api (#3306)
SDK API changes ⚠️:
- RegistrationWizard.createAccount() parameters are now all optional, following Matrix spec (#3205)
@ -35,9 +66,7 @@ SDK API changes ⚠️:
Build 🧱:
- Upgrade to gradle 7
- https://github.com/Piasy/BigImageViewer is now hosted on mavenCentral()
Test:
-
- Upgrade Realm to version 10.4.0
Other changes:
- New store descriptions
@ -70,7 +99,7 @@ Changes in Element 1.1.4 (2021-04-09)
Improvements 🙌:
- Split network request `/keys/query` into smaller requests (250 users max) (#2925)
- Crypto improvement | Bulk send NO_OLM withheld code
- Crypto improvement | Bulk send NO_OLM withheld code
- Display the room shield in all room setting screens
- Improve message with Emoji only detection (#3017)
- Picture preview when replying. Also add the image preview in the message detail bottomsheet (#2916)
@ -629,7 +658,7 @@ Improvements 🙌:
- Sending events is now retried only 3 times, so we avoid blocking the sending queue too long.
- Display warning when fail to send events in room list
- Improve UI of edit role action in member profile
- Moderation | New screen to display list of banned users in room settings, with unban action
- Moderation | New screen to display list of banned users in room settings, with unban action
Bugfix 🐛:
- Fix theme issue on Room directory screen (#1613)

View File

@ -47,7 +47,7 @@ android {
}
dependencies {
implementation 'com.github.chrisbanes:PhotoView:2.1.4'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

View File

@ -2,8 +2,8 @@
buildscript {
// Ref: https://kotlinlang.org/releases.html
ext.kotlin_version = '1.4.32'
ext.kotlin_coroutines_version = "1.4.2"
ext.kotlin_version = '1.5.0'
ext.kotlin_coroutines_version = "1.5.0-RC"
repositories {
google()
jcenter()
@ -12,10 +12,10 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.google.gms:google-services:4.3.5'
classpath 'com.android.tools.build:gradle:4.2.1'
classpath 'com.google.gms:google-services:4.3.8'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.2.0'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
classpath "com.likethesalad.android:string-reference:1.2.2"

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: vylepšení výkonnosti a opravy chyb!
Úplný záznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.2

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: vylepšení výkonnosti a opravy chyb!
Úplný záznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.3

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: vylepšení výkonnosti a opravy chyb!
Úplný záznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: nutné opravy pro 1.1.4
Úplný záznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: nutné opravy chyb pro 1.1.5!
Úplný záznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1 +1 @@
Zabezpečený decentralizovaný chat a VoIP. Uchovejte svá data v bezpečí.
Skupinový messenger - šifrovaná komunikace, skupinový chat a video hovory

View File

@ -1 +1 @@
Element (dříve Riot.im)
Element - bezpečný messenger

View File

@ -1,2 +1,2 @@
Diese neue Version enthält hauptsächlich Verbesserungen der Benutzeroberfläche und der Handhabung. Du kannst jetzt ganz schnell Freund*innen einladen und DMs erstellen, indem du schlicht einen QR-Code scannst.
Diese neue Version enthält hauptsächlich Verbesserungen der Benutzeroberfläche und der Handhabung. Du kannst jetzt ganz schnell Freunde einladen und DMs erstellen, indem du schlicht einen QR-Code scannst.
Vollständige Versionshinweise: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View File

@ -0,0 +1,2 @@
Hauptänderungen in dieser Version: Leistungsverbesserung und Fehlerbehebungen!
Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Hauptänderungen in dieser Version: Wichtige Fehlerbehebungen für 1.1.4!
Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Hauptänderungen in dieser Version: Wichtige Fehlerbehebungen für 1.1.5!
Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1,30 +1,39 @@
Element ist eine neuartige Messaging- und Kollaborationsapp:
Element ist mehr als ein sicherer Messenger. Es ist ein produktives Kolaborationsapp für das Team und eignet sich ideal für den Gruppenchat beim Arbeiten von zuhause aus. Mit eingebauter Ende-zu-Ende-Verschlüsselung ermöglicht Element umfangreiche und sichere Videokonferenzen, das Teilen von Dokumenten/Dateien und Videoanrufe.
1. Volle Kontrolle über deine Privatssphäre
2. Kommuniziere mit jedem aus dem Matrix-Netzwerk und mit der Integration von z.B. Slack sogar über Matrix hinaus
3. Schutz vor Werbung, Datamining und geschlossenen Platformen
4. Absicherung durch Ende-zu-Ende-Verschlüsselung, und Cross-Signing um andere zu verifizieren
<b>Element enthält folgende Funktionen:</b>
- Fortgeschrittene Werkzeuge für die Online-Kommunikation
- Vollverschlüsselte Nachrichten um eine sichere Kommunikation innerhalb und außerhalb des Unternehmens zu ermöglichen
- Dezentralisierte Chats basierend auf das quelloffene Matrix-Framework
- Sichere und kontrollierte Dateienfreigabe durch verschlüsselte Daten beim verwalten von Projekten
- Videochats über VoIP und Bildschirmübertragung
- Einfache Einbindungen mit Ihren favorisierten Online-Kolaborationswerkzeugen, Projektverwaltungswerkzeugen, VoIP-Diensten und andere Kommunikationsapps für Ihren Team
Element unterscheidet sich durch Dezentralität und Open Source deutlich von anderen Messaging- und Kollaborationsapps.
Element unterscheidet sich deutlich von anderen Kommunikations- und Kollaborationsapps. Es läuft auf Matrix, ein offenes Netzwerk für eine sichere und dezentralisierte Kommunikation. Es erlaubt den Nutzern ihre eigenen Matrix-Dienste zu betreiben und gibt ihnen damit die vollständige Kontrolle und Besitz über ihre eigenen Daten und Nachrichten.
Element ermöglicht es einen eigenen Server zu betreiben - oder einen beliebigen auszuwählen, sodass du nicht nur Privatssphäre gewinnst, sondern auch deine Daten und Konversationen in deiner Hand sind und du sie kontrollieren kannst. Du hast Zugriff auf ein offenes Netzwerk, und kannst daher nicht nur mit Element-Nutzern schreiben. Und es ist sehr sicher.
<b>Privatsphäre/Datenschutz und verschlüsselte Kommunikation</b>
Element schützt Ihnen vor unerwünschte Werbung, das Datenschürfen und geschlossene unentkommbare Dienste. Auch schützt es all Ihre Daten, Video und Sprachkommunikation unter vier Augen durch Ende-zu-Ende-Verschlüsselung und das Quersignieren von Geräten zur Verifizierung.
Element ist zu all diesem in der Lage, weil es Matrix nutzt - einen Standard für offene, dezentrale Kommunikation.
Element gibt Ihnen die Kontrolle über Ihre Privatsphäre, während es Ihnen ermöglicht mit jeden auf dem Matrix-Netzwerk oder andere geschäftliche Kollaborationswerkzeuge durch das Einbinden von Apps wie Slack sicher zu kommunizieren.
Element gibt dir die Kontrolle, indem es dir die Wahl darüber lässt, wer deine Konversationen hostet. In der Element-App kannst du zwischen verschiedenen Möglichkeiten auswählen:
<b>Element kann man selber betreiben</b>
Um mehr Kontrolle über Ihre sensiblen Daten und Konversationen zu ermöglichen, kann man Element selbst betreiben oder Sie wählen irgendeinen Matrix-basierten Dienst - der standard für quelloffene, dezentralisierte Kommunikation. Element gibt Ihnen Privatsphäre, Sicherheitskonformität und die Flexibilität zum Integrieren.
<b>Besitzen Sie Ihre Daten</b>
Sie entscheiden wo Sie Ihre Daten und Nachrichten aufbewahren, ohne das Risiko des Datenschürfens oder des Zugriffes Dritter.
Element gibt Ihnen die Kontrolle durch verschiedene Wege:
1. Kostenlos auf dem öffentlichen matrix.org Server registrieren, der von den Matrix-Entwicklern gehostet wird, oder wähle aus Tausenden von öffentlichen Servern, die von Freiwilligen gehostet werden
2. Einen Konto auf einem eigenen Server auf eigener Hardware betreiben
2. Einen Konto auf einem eigenen Server in der eigenen IT-Infrastruktur betreiben
3. Einen Konto auf einem benutzerdefinierten Server erstellen, zum Beispiel durch ein Abonnement bei Element Matrix Services (kurz EMS)
<b>Wieso Element nutzen?</b>
<b>Offene Kommunikation und Zusammenarbeit</b>
Sie können mit jeden auf dem Matrix-Netzwerk chatten, egal ob sie Element, eine Matrix-App oder sogar eine andere Kommunikationsapp nutzen.
<b>BESITZE DEINE DATEN</b>: Du entscheidest wo deine Daten und Nachrichten gespeichert werden. Du besitzt und kontrollierst sie, anstatt ein Großkonzern, der deine Daten analysiert und Dritten Zugriff gibt.
<b>Super sicher</b>
Reale Ende-zu-Ende-Verschlüsselung (nur die Personen in der Konversation können die Nachricht entschüsseln) und Quersignierung von Geräten zur Verifizierung.
<b>OFFENE KOMMUNIKATION UND KOLLABORATION</b>: Du kannst mit jedem im Matrix-Netzwerk schreiben, ob sie nun Element oder eine andere Matrix-App nutzen, oder gar ein anderes Kommunikationssystem wie z.B. Slack, IRC oder XMPP.
<b>Vollständige Kommunikation und Integration</b>
Kurznachrichten, Sprach- und Videoanrufe, kontrollierte Dateifreigaben, Bildschirmübertragungen und eine ganze Reihe an Integrationen, Bots and Widgets. Schaffe Räume, Gemeinschaften, bleibe auf dem Laufenden und erledige Sachen.
<b>SUPER SICHER</b>: Echte Ende-zu-Ende-Verschlüsselung (nur Personen in der Konversation können die Nachrichten entschlüsseln), und Cross-Signing um die Geräte der anderen Personen zu verifizieren.
<b>VOLLSTÄNDIGE KOMMUNIKATION</b>: Nachrichten, Telefonate und Videoanrufe, Teilen von Dateien oder dem eigenen Bildschirm und viele andere Integrationen, Bots und Widgets. Erstelle Räume, Communities, bleib in Kontakt und sei produktiv.
<b>ÜBERALL WO DU BIST</b>: Bleib in Kontakt wo auch immer du bist - mit einem vollständig synchronisierten Nachrichtenverlauf über alle Geräte und im Netz auf https://app.element.io.
<b>Das Stehengelassene später wieder aufgreifen</b>
Bleibe auf dem Laufenden, egal wo Sie sind, mit vollständig synchronisierter Nachrichtenverlauf quer über all Ihrer Geräte und im Netz auf https://app.element.io

View File

@ -1 +1 @@
Sicherer dezentraler Chat und Telefonie. Schütze deine Daten vor Dritten.
Gruppen-Messenger - verschlüsselte Kommunikation, Gruppenchat und Videoanrufe

View File

@ -1 +1 @@
Element (zuvor Riot.im)
Element - Sicherer Messenger

View File

@ -0,0 +1,2 @@
Main changes in this version: beta support for Spaces. Compress video before sending.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.7

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: jõudluse parandused ja pisikohendused.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: kiirparandused versioon 1.1.4 jaoks.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: kiirparandused versioon 1.1.5 jaoks.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1,30 +1,39 @@
Element on uut tüüpi suhtlus- ja koostöörakendus, mis:
Element on nii suhtlus- ja koostöörakendus, mis sobib ideaalselt rühmavestlusteks kaugtöö ajal. Läbiv krüptimine on kasutusel sõnumivahetuseks, videokõnedeks, häälkõnedeks ja failide jagamiseks.
1. Võimaldab täielikku kontrolli privaatsuse üle
2. Võimaldab suhelda kõigiga Matrixi võrgus ja isegi väljaspool seda, olles integreeritud selliste rakendustega nagu Slack
3. Kaitseb sind reklaamide ja andmekogumise eest
4. Tagab turvalisuse läbiva krüptimise abil, kasutades risttunnustamist vestlejate tuvastamiseks
<b>Element pakub muu hulgas selliseid võimalusi</b>
- moodsad võrgupõhised suhtlusvahendid
- läbivalt krüptitud sõnumid võimaldavad turvalist suhtlust, sealhulgas kaugtöötajatega
- Matrix'i protokollil põhinev hajutatud suhtlusvõrk
- projektide jaoks vajalike failide jagamine turvaliselt ja krüptitult
- VoIP'i põhised videokõned ja ekraani jagamine
- lihtne lõimimine harjumispäraste võrgupõhiste koostöövahenditega, projektihalduse rakendustega, VoIP-teenustega ja muude ühistöörakendustega
Element erineb täielikult teistest sõnumside- ja koostöörakendustest, kuna see on detsentraliseeritud ja avatud lähtekoodiga.
Element erineb teistest sõnumi- ja koostöörakendustest. Tema aluseks on Matrix - avatud võrk turvalise ja hajutatud suhtluse jaoks.
Element võimaldab ise hostida - või valida hosti -, et oleks tagatud privaatsus ja kontroll oma andmete ja vestluste üle. Element annab ka juurdepääsu avatud võrgule, nii et te ei pea vaid Elemendi kasutajatega rääkima. Ning kogu süsteem on väga turvaline.
<b>Privaatsus ja krüptitud sõnumivahetus</b>
Element tagab, et sa ei ole seotud reklaamidega, andmekogumisega ja suletud süsteemidega. Kasutades läbivat krüptimist ja risttunnustamisel põhinevat verifitseerimist on sinu sõnumid, andmed, kahepoolsed videokõned ja häälkõned turvatud.
Element töötab Matrixil - avatud, detsentraliseeritud suhtlus-standardil.
Lubades suhelda turvaliselt ükspuha kellega Matrix'i võrgus või muude ärikeskondades kasutatavate koostöörakendustega nagu Slack, jätab Element sulle kontrolli oma privaatsuse üle.
Võimaldades valida, kes vestlusi korraldab, annab Element annab kontrolli sinule. Rakendust Element saad kasutada mitmel viisil.
<b>Võid kasutada Element'i jaoks oma serverid</b>
Kui vajad suuremat kontrolli oma suhtluse ja andmete üle, siis võid kasutada oma serverit või tellida teenuse ükspuha missuguselt Matrix'i-teenuse pakkujalt. Matrix on standard avatud lähtekoodil põhineva detsentraliseeritud suhtluse jaoks.
1. Tasuta konto Matrixi arendajate hostitud avalikus serveris matrix.org või vali tuhandete avalike serverite hulgast, mida haldavad vabatahtlikud
2. Hosti oma kontot ise, paigaldades serveri oma riistvarale
3. Registreeruge serveris olevale kontole, tellides Element Matrix Services teenuseplatvormi
<b>Andmed on Sinu omad</b>
Sina otsustad seda, kus hoiad oma sõnumeid ja andmeid. Ning seejuures puudub andmekaevandamise risk ja ligipääs kolmandatele osapooltele.
<b> Miks valida element? </b>
Element annab kontrolli sinule valikuga mitme võimaluse vahel:
1. tasuta konto Matrix'i arendajate hostitud avalikus serveris matrix.org või vali tuhandete avalike serverite hulgast, mida haldavad vabatahtlikud
2. hosti oma kontot ise, paigaldades serveri oma IT-taristule
3. telli tasuline kasutajakonto Element Matrix Services teenuseplatvormilt
<b> KONTROLL ANDMETE ÜLE</b>: otsustad ise, kus oma andmeid ja sõnumeid hoida. Need kuuluvad sulle ja sinu käes on kontroll, mitte mõne MEGAFIRMA käes, mis andmeid oma kasuks kaevandab või kolmandatele isikutele juurdepääsu annab.
<b>Avatud suhtlus ja koostöö</b>
Saad vestelda kõigi teistega Matrix'i võrgus, olenemata sellest, kas nad kasutavad Elementi või mõnda muud Matrixi rakendust ja isegi kui nad kasutavad mõnda teistsugust suhtlussüsteemi.
<b> AVATUD SUHTLUS JA KOOSTÖÖ </b>: saad vestelda kõigi teistega Matrixi võrgus, olenemata sellest, kas nad kasutavad Elementi või mõnda muud Matrixi rakendust, ja isegi kui nad kasutavad teistsugust suhtlussüsteemi nagu Slack, IRC või XMPP.
<b>Üliturvaline</b>
Tõeline läbiv krüptimine (ainult vestluses osalejad saavad sõnumeid lugeda) ja risttunnustamine vestluses osalejate tuvastamiseks.
<b> ÜLITURVALINE </b>: tõeline läbiv krüptimine (ainult vestluses osalejad saavad sõnumeid lugeda) ja risttunnustamine vestluses osalejate tuvastamiseks.
<b>Kõik suhtlusvõimalused</b>
Sõnumid, hääl- ja videokõned, failide jagamine, ekraani jagamine ja terve hulk lõiminguid, roboteid ja vidinaid. Loo tubasid, kogukondi, hoia ühendust ja saa asjad aetud.
<b> KÕIK SUHTLUSVÕIMALUSED</b>: sõnumid, hääl- ja videokõned, failide jagamine, ekraani jagamine ja terve hulk lõiminguid, roboteid ja vidinaid. Loo tubasid, kogukondi, hoia ühendust ja saa asjad aetud.
<b> KÕIKJAL, KUS VIIBITE</b>: saad suhelda kõigis oma seadmetes ja ka veebis aadressil https://app.element.io ning sealjuures täielikult sünkroonitud sõnumite ajalooga.
<b>Jätka sealt, kus pooleli jäid</b>
Saad suhelda kõigis oma seadmetes ja ka veebis aadressil https://app.element.io ning sealjuures täielikult sünkroonitud sõnumite ajalooga.

View File

@ -1 +1 @@
Turvalised ning hajutatud vestlused ja VoIP-kõned. Sinu suhtlus on üliturvaline.
Vestlus- ja koostöörakendus: krüptitud sõnumid, rühmavestlused ja videokõned

View File

@ -1 +1 @@
Element (varem Riot.im)
Element - turvaline sõnumiklient

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : amélioration des performances et corrections de bugs !
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : Corrections de bugs sur la 1.1.4
Liste de tous les changements : https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : Corrections de bugs sur la 1.1.5
Liste de tous les changements : https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1 +1 @@
Chat & VoIP sûr et décentralisé. Gardez vos données en sécurité.
Messagerie de groupes - messages chiffrés, groupés et appels vidéos

View File

@ -1 +1 @@
Element (anciennement Riot.im)
Element - Messagerie sécurisée

View File

@ -0,0 +1,2 @@
Disse nije ferzje bestjit foar in grut diel út breksoplossings en ferbetteringen. Berjochten stjoere giet no in soad flugger.
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.10

View File

@ -0,0 +1,2 @@
Disse nije ferzje bestjit foar in grut diel út brûkersinterfaasje en brûkersûnderfingsferbetteringen. No kinst freonen útnûgje, en gau DM's meitsje troch QR koades te scannen.
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: URL ynsjen, nij emoji toetseboerd, nij keamer ynstellings moochlikheden, en snie foar kryst.
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.12

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: URL ynsjen, nij emoji toetseboerd, nij keamer ynstellings moochlikheden, en snie foar kryst.
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.13

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Keamer fjochten feroarje, automatysk ljocht/tsjuster tema, en breksferbetteringen.
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.14

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Stipe foar sosjaal ynlogge!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.15

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Stipe foar sosjaal ynlogge!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.15 and https://github.com/vector-im/element-android/releases/tag/v1.0.16

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Breksoplossings!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.17

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: VoIP (lûds en video skilje yn DM) ferbetteringen en breksoplossings!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.0

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Prestaasje feroaringen en breksoplossings!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.1

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Prestaasje feroaringen en breksoplossings!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.2

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Prestaasje feroaringen en breksoplossings!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.3

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: Prestaasje feroaringen en breksoplossings!
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: feroaringen foar 1.1.4
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Haadferoaring yn disse ferzje: feroaringen foar 1.1.5
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -0,0 +1 @@
Groepsberjochtetsjinst - fersifere berjochten, groeps petearen en fideo skilje

View File

@ -0,0 +1 @@
Element - Feilige Berjochtetsjinst

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: prestazioni migliorate e correzione di errori!
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: correzioni per la 1.1.4
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: correzioni per la 1.1.5
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1,30 +1,39 @@
Element è un nuovo tipo di app di messaggistica e collaborazione che:
Element è sia un messenger sicuro sia un'app collaborativa per team di produttività, ideale per chat di gruppo durante il lavoro da remoto. Questa app usa una crittografia end-to-end per fornire videoconferenze, condivisione di file e videochiamate.
1. Ti mette al controllo per preservare la tua privacy
2. Ti lascia comunicare con chiunque nella rete Matrix e oltre, integrandosi con app come Slack
3. Ti protegge da pubblicità, raccolta di dati e piattaforme chiuse
4. Ti protegge con la crittografia end-to-end, con la firma incrociata per verificare gli altri
<b>Tra le caratteristiche di Element ci sono:</b>
- Strumenti di comunicazione online avanzati
- Messaggi totalmente cifrati per consentire comunicazioni aziendali più sicure, anche per i lavoratori remoti
- Chat decentralizzate basate sull'infrastruttura open source Matrix
- Condivisione sicura di file con dati crittografati durante la gestione dei progetti
- Videochiamate con "Voice over IP" e condivisione dello schermo
- Facile integrazione con i tuoi strumenti collaborativi online preferiti, strumenti di gestione progetti, servizi VoIP ed altre app di messaggistica tra team
Element è completamente diverso dalle altre app di messaggistica e collaborazione perchè è decentralizzato e open source.
Element è completamente diverso dalle altre app di messaggistica e collaborazione. Funziona su Matrix, una rete aperta per messaggi sicuri e comunicazioni decentralizzate. Può essere installato in locale per dare agli utenti il pieno possesso e controllo dei propri dati e messaggi.
Element può essere gestito in locale - o puoi scegliere un host - in modo che tu abbia privacy, possesso e controllo dei tuoi dati e conversazioni. Ti dà accesso ad una rete aperta, quindi non sei limitato a parlare solo con altri utenti Element. Ed è molto sicuro.
<b>Privacy e messaggi privati</b>
Element ti protegge da pubblicità indesiderate, dalla raccolta di dati e dalle piattaforme chiuse. Protegge tutti i tuoi dati e comunicazioni uno-ad-uno, attraverso la crittografia end-to-end e la verifica a firma incrociata tra dispositivi.
Element può fare tutto ciò perchè funziona su Matrix - lo standard per comunicazioni aperte e decentralizzate.
Element ti dà il controllo della tua privacy consentendoti di comunicazre in modo sicuro con chiunque nella rete di Matrix, o con altri strumenti collaborativi aziendali, integrandosi con app come Slack.
Element ti mette al controllo lasciandoti scegliere chi gestisce il server delle tue conversazioni. Dall'app Element, hai diverse opzioni:
<b>Element può essere installato in locale</b>
Per consentire un maggiore controllo dei tuoi dati sensibili e delle conversazioni, Element può essere gestito in locale o puoi scegliere un qualsiasi host basato su Matrix - lo standard per le comunicazioni open source e decentralizzate. Element ti offre privacy, conformità alla sicurezza e flessibilità di integrazione.
<b>Possiedi i tuoi dati</b>
Decidi tu dove tenere i tuoi dati e messaggi. Senza il rischio di raccolta di dati o accessi da terze parti.
Element ti mette al controllo in diversi modi:
1. Crea un account gratuito sul server pubblico matrix.org gestito dagli sviluppatori di Matrix, o scegli tra migliaia di server pubblici gestiti da volontari
2. Gestisci autonomamente un account installando un server sul tuo hardware
2. Gestisci autonomamente un account installando un server nella tua infrastruttura informatica
3. Registra un account su un server personalizzato iscrivendoti alla piattaforma Element Matrix Services
<b>Perchè scegliere Element?</b>
<b>Messaggistica e collaborazioni aperte</b>
Puoi chattare con chiunque nella rete Matrix, sia che stiano usando Element, un'altra app Matrix, o anche un'app di messaggistica diversa.
<b>POSSIEDI I TUOI DATI</b>: decidi dove tenere i tuoi dati e messaggi. Sono tuoi e li controlli tu, non qualche MEGADITTA che raccoglie i tuoi dati o ne dà l'accesso a terze parti.
<b>Super sicuro</b>
Vera crittografia end-to-end (solo chi è nella conversazione può decifrare i messaggi) e verifica di dispositivi a firma incrociata.
<b>MESSAGGISTICA E COLLABORAZIONE APERTE</b>: puoi chattare con chiunque nella rete Matrix, usando Element o un'altra app Matrix, o anche se si sta usando un sistema di messaggistica diverso come Slack, IRC o XMPP.
<b>Comunicazioni ed integrazioni complete</b>
Messaggi, chiamate audio e video, condivisione file e schermo, un vasto numero di integrazioni, bot e widget. Crea stanze, comunità, resta in contatto e porta a termine gli obiettivi.
<b>SUPER SICURO</b>: vera crittografia end-to-end (solo chi è nella conversazione può decifrare i messaggi) e firma incrociata per verificare i dispositivi dei partecipanti.
<b>COMUNICAZIONE COMPLETA</b>: messaggi, chiamate audio e video, condivisione file e schermo, un vasto numero di integrazioni, bot e widget. Crea stanze, comunità, resta in contatto e porta a termine gli impegni.
<b>OVUNQUE TU SIA</b>: resta in contatto ovunque tu sia con la cronologia dei messaggi sincronizzata tra tutti i tuoi dispositivi e in rete su https://app.element.io.
<b>Riprendi da dove ti eri fermato</b>
Resta in contatto ovunque tu sia con la cronologia dei messaggi sincronizzata tra tutti i tuoi dispositivi e in rete su https://app.element.io

View File

@ -1 +1 @@
Chat e VoIP decentralizzati sicuri. Tieni lontani i tuoi dati dalle terze parti.
Messenger di gruppo - messaggi cifrati, chat di gruppo e videochiamate

View File

@ -1 +1 @@
Element (ex Riot.im)
Element - Messaggi sicuri

View File

@ -0,0 +1,2 @@
Huvudsakliga ändringar i den här versionen: prestandaförbättringar och buggfixar!
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
Huvudsakliga ändringar i den här versionen: hotfixar för 1.1.4
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
Huvudsakliga ändringar i den här versionen: hotfixar för 1.1.5
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1,30 +1,39 @@
Element är en ny sorts meddelande- och samarbetsapp som:
Element är både en säker meddelandeapp och en samarbetsapp för produktivitet som är ideal för gruppchattar vid distansarbete. Appen använder totalsträckskryptering för att tillhandahålla kraftfulla videogruppsamtal, fildelning och röstsamtal.
1. Sätter dig i kontroll för att kunna säkerställa ditt privatliv
2. Låter dig kommunicera med vem som helst i Matrix-nätverket, och till och med bortom det genom integrationer med appar som Slack
3. Skyddar dig från reklam, datainsamling och inlåsning
4. Säkrar dig genom totalsträckskryptering, med korssingering för att verifiera andra
<b>Elements funktioner inkluderar:</b>
- Avancerade kommunikationsverktyg
- Fullt krypterade meddelanden för att tillåta säkrare företagskommunikation, även för distansarbetare
- Decentraliserad chatt baserad på det öppna ramverket Matrix
- Säker fildelning med krypterad data vid hantering av projekt
- Videochattar med Voice over IP och skärmdelning
- Enkel integration med dina föredragna onlinesamarbetsverktyg, projektledningsverktyg, VoIP-tjänster och andra teammeddelandeappar
Element skiljer sig helt från andra meddelande- och samarbetsappar genom att vara decentraliserad och öppen källkod.
Element är helt olik andra meddelande- och samarbetsappar. Den använder Matrix, ett öppet nätverk för säkra meddelanden och decentraliserad kommunikation. Den låter dig driva en igen server för att ge användare maximalt ägandeskap över sin data och sina meddelanden.
Element låter dig driva en egen server - eller välja en värd - så att du har sekretess, ägande och kontroll över din data och dina konversationer. Den ger dig tillgång till ett öppet nätverk; så att du inte kan prata bara med Element-användare. Och den är väldigt säker.
<b>Sekretess och krypterade meddelanden</b>
Element skyddar dig från oönskad reklam, datainsamling och inlåsning. Den skyddar även all din data, en-till-en-video- och röstkommunikation genom totalsträckskryptering och korssignerad enhetsverifiering.
Element kan göra allt detta för att den använder Matrix - standarden för öppen decentraliserad kommunikation.
Element sätter dig i kontroll över ditt privatliv och låter dig kommunicera säkert med vem som helst i Matrix-nätverket, eller andra samarbetsverktyg genom att integrera med appar som Slack.
Element sätter dig i kontroll genom att låta dig välja att vara värd för dina konversationer. Från appen Element kan du välja att ansluta på följande sätt:
<b>Du kan driva Element själv</b>
För att tillåta större kontroll över din känsliga data och konversationer, så kan du lägga Element på en egen server eller använda valfri Matrix-baserad värd - standarden för open source-baserad, decentraliserad kommunikation. Element ger dig sekretess, säkerhetsefterlevnad och integrationsflexibilitet.
1. Skaffa ett gratis konto på den publika servern på matrix.org, vilken drivs av Matrix-utvecklarna, eller välj bland tusentals offentliga servrar som drivs av volontärer
2. Var värd för ditt eget konto genom att driva en server på din egen hårdvara
3. Skapa ett konto på en anpassad server genom att registrera dig på värdplattformen Element Matrix Services
<b>Äg din data</b>
Du bestämmer vart du vill lagra din data och dina meddelanden, utan rist för datainsamling eller åtkomst av tredje parter.
<b>Varför välja Element?</b>
Element sätter dig i kontroll på olika sätt:
1. Få ett gratiskonto på den offentliga servern matrix.org som drivas av Matrixutvecklarna, eller välj bland tusentals offentliga servrar som drivs av volontärer
2. Självdriv ditt konto genom att driva en server på din egen IT-infrastruktur
3. Skapa ett konto på en anpassad server genom att abonnera på värdplattformen Element Matrix Services
<b>ÄG DIN DATA</b>: Du väljer var du vill ha din data och dina meddelanden. Du äger den och kontrollerar den, inte nåt stort företag som samlar in din data och ger den till tredje parter.
<b>Öppen meddelandehantering och kommunikation</b>
Du kan chatta med vem som helst i Matrix-nätverket, oavsett om de använder Matrix, en annan Matrix-app eller till och med en annan meddelandeapp.
<b>ÖPPEN KOMMUNIKATION OCH ÖPPET SAMARBETE</b>: Du kan chatta med vem som helst på Matrix-nätverket, oavsett om de använder Element eller en annan Matrix-app, och till och med om de använder ett annat meddelandesystem som Slack, IRC eller XMPP.
<b>Supersäker</b>
Riktig totalsträckskryptering (bara de i konversationen kan avkryptera meddelanden), och korssignerad enhetsverifiering.
<b>SUPERSÄKER</b>: Riktig totalsträckskryptering (bara de in konversationen kan avkryptera meddelandena), och korssingering för att verifiera konversationsmedlemmars enheter.
<b>Komplett kommunikation och integration</b>
Meddelanden, röst- och videosamtal, fildelning, skärmdelning och massa integrationer, bottar och widgets. Bygg rum och gemenskaper, håll kontakten och få saker gjorda.
<b>KOMPLETT KOMMUNIKATION</b>: Meddelanden, röst- och videosamtal, fildelning, skärmdelning och massa integrationer, bottar och widgets. Skapa rum och gemenskaper, håll kontakten och få saker gjorda.
<b>ÖVERALLT DÄR DU ÄR</b>: Håll kontakten vart du än befinner dig med fullständigt synkroniserad meddelandehistorik på alla dina enheter och på webben på https://app.element.io.
<b>Fortsätt där du lämnade</b>
Håll kontakten vart du än är med fullt synkroniserad meddelandehistorik på alla dina enheter och på webben på https://app.element.io

View File

@ -1 +1 @@
Säker decentraliserad chatt och VoIP. Håll din data säker från tredje parter.
Gruppmeddelandeapp - krypterade meddelanden, gruppchatt och videosamtal

View File

@ -1 +1 @@
Element (före detta Riot.im)
Element - Säker meddelandeapp

View File

@ -0,0 +1,2 @@
此版本中的主要變動:效能改善與錯誤修復!
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.1.4

View File

@ -0,0 +1,2 @@
此版本中的主要變動1.1.4 的快速修補
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.1.5

View File

@ -0,0 +1,2 @@
此版本中的主要變動1.1.5 的快速修補
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.1.6

View File

@ -1,30 +1,39 @@
Element 是一種新型態的即時通訊軟體與協作應用程式:
Element 同時是安全的通訊軟體,也是生產力團隊協作應用程式,非常適合在遠端工作時進行群組聊天。此聊天應用程式使用了端到端加密來提供強大的視訊會議、檔案分享與語音通話。
1. 自己的隱私自己掌控
2. 讓您與任何在 Matrix 網路中的人通訊,甚至可與如 Slack 等的應用程式整合
3. 保護您免受廣告、資料採礦與圍牆花園的侵害
4. 透過端到端加密保護您,並使用交叉簽章來驗證其他人
<b>Element 的功能包含了:</b>
- 進階線上通訊工具
- 完全加密的訊息,即使對於遠端工作者,也可以有更安全的公司通訊
- 以 Matrix 開放原始碼框架為基礎的去中心化的聊天
- 在管理專案時透過加密資料安全地分享檔案
- 包含了 VoIP 與畫面分享的視訊聊天
- 與您最喜歡的協作工具、專案管理工具、VoIP 服務與其他團隊訊息應用程式輕鬆整合
Element 是去中心化且開放原始碼的應用程式,因此與其他即時通訊與協作軟體完全不同。
Element 與其他訊息傳遞與協作應用程式完全不同。它在 Matrix一個用於安全傳遞訊息與去中心化通訊的開放網路上執行。其可以自架讓使用者對他們的資料與訊息有最大的所有權與控制權
Element 讓您可以自架(或是自行選擇服務提供者)所以您擁有您資料與對話的隱私、所有權與控制權。它讓您可以存取開放的網路;因此,您不僅可以與其他 Matrix 使用者聊天。而且非常安全。
<b>隱私與加密訊息傳遞</b>
Element 保護您不受不想要的廣告、資料挖礦與圍牆花園侵擾。其也透過端到端加密與交叉簽章裝置驗證保護了您所有的資料,並提供一對一視訊以及語音通訊。
Element 能作到這些事情是因為它在 Matrix 上執行,這是一個開放的去中心化通訊的標準
Element 透過與其他商業協作工具,如 Slack 等應用程式整合,讓您可以在控制您的隱私的同時,也可以與 Matrix 網路上的任何人安全地通訊
Element 讓您選擇您要在哪裡託管您的對話來將控制權還給您。在 Element 應用程式中,您可以選擇其他方式來託管:
<b>Element 可以自架</b>
為了可以完整控制您的敏感資料與對話Element 可以自架,您也可以選擇任何以 Matrix 為基礎的服務提供商開放原始碼、去中心化的通訊標準。Element 為您提供隱私、安全合規與整合活性。
1. 在由 Matrix 開發者架設的 matrix.org 公開伺服器上取得免費的帳號,或是從數千個由志願者所架設的公開伺服器中選擇
2. 在您自己的硬體上自行架設伺服器並建立帳號
3. 訂閱 Element Matrix 服務託管平台並在自訂伺服氣上註冊帳號
<b>擁有您的資料</b>
您可以決定將您的資料與訊息儲存在何處。沒有資料挖礦或被第三方存取的風險。
<b>為何選擇 Element</b>
Element 透過不同的方式讓您掌控一切:
1. 在 Matrix 開發者架設的 matrix.org 公開伺服器上取得免費帳號,或是從數千個由志願者架設的公開伺服器中選擇
2. 在您自己的 IT 基礎架構上執行伺服器來自行託管您的帳號
3. 只要訂閱 Element Matrix Services 託管平台就可以在自訂的伺服器上註冊帳號
<b>擁有您的資料</b>:您決定您的資料與訊息要放在哪裡。您擁有並控制它,而非某些科技巨頭會挖掘您的資料並將其售予第三方。
<b>開放訊息傳遞與協作</b>
您可以與 Matrix 網路上的任何人聊天,不論他們是使用 Element、其他 Matrix 應用程式或其他通訊應用程式。
<b>開放的即時通訊與協作</b>:您可以與 Matrix 網路中的任何人聊天,不管他們是使用 Element 或其他 Matrix 應用程式都可以,或甚至是其他的訊息系統,如 Slack、IRC 或 XMPP 也都可以。
<b>超級安全</b>
真的端到端加密(僅有那些在對話中的可以解密訊息)以及交叉簽章裝置驗證。
<b>超級安全</b>:即時的端到端加密(僅有參與對話的人可以解密訊息),以及交叉簽章以驗證對話參與者的裝置。
<b>完整的通訊與整合Complete communication and integration</b>
訊息傳遞、語音與視訊通話、檔案分享、畫面分享與超多的整合、機器人與小工具。建構聊天室、社群、保持聯絡並完成工作。
<b>完整通訊</b>:即時通訊、語音與視訊通話、檔案分享、畫面分享與超多的整合、機器人與小工具。建立聊天室、保持聯繫並完成工作。
<b>無論您身在何處</b>:無論您身在何處,都可以透過 https://app.element.io 來在所有裝置與網路上保持訊息歷史同步。
<b>從上次離開的地方開始</b>
無論您身在何處,都可以透過在您所有裝置與網頁 https://app.element.io 間完全同步的訊息歷史保持聯絡

View File

@ -1 +1 @@
安全的去中心化聊天與 VoIP。確保您的資料不受第三方的影響。
群組通訊軟體 - 訊息加密、群組聊天與視訊通話

View File

@ -1 +1 @@
Element(曾名為 Riot.im
Element - 安全的通訊軟體

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=81003f83b0056d20eedf48cddd4f52a9813163d4ba185bcf8abd34b8eeea4cbd
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
distributionSha256Sum=13bf8d3cf8eeeb5770d19741a59bde9bd966dd78d17f1bbad787a05ef19d1c2d
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -6,13 +6,10 @@ apply plugin: 'realm-android'
buildscript {
repositories {
// mavenCentral()
//noinspection GrDeprecatedAPIUsage
jcenter()
mavenCentral()
}
dependencies {
// Stick to this version until https://github.com/realm/realm-java/issues/7402 is fixed
classpath "io.realm:realm-gradle-plugin:10.3.1"
classpath "io.realm:realm-gradle-plugin:10.5.0"
}
}
@ -115,7 +112,7 @@ dependencies {
def lifecycle_version = '2.2.0'
def arch_version = '2.1.0'
def markwon_version = '3.1.0'
def daggerVersion = '2.35'
def daggerVersion = '2.35.1'
def work_version = '2.5.0'
def retrofit_version = '2.9.0'
@ -172,7 +169,7 @@ dependencies {
implementation 'com.otaliastudios:transcoder:0.10.3'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.22'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.23'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.5.1'

View File

@ -226,12 +226,12 @@ class QrCodeTest : InstrumentedTest {
private fun checkHeader(byteArray: ByteArray) {
// MATRIX
byteArray[0] shouldBeEqualTo 'M'.toByte()
byteArray[1] shouldBeEqualTo 'A'.toByte()
byteArray[2] shouldBeEqualTo 'T'.toByte()
byteArray[3] shouldBeEqualTo 'R'.toByte()
byteArray[4] shouldBeEqualTo 'I'.toByte()
byteArray[5] shouldBeEqualTo 'X'.toByte()
byteArray[0] shouldBeEqualTo 'M'.code.toByte()
byteArray[1] shouldBeEqualTo 'A'.code.toByte()
byteArray[2] shouldBeEqualTo 'T'.code.toByte()
byteArray[3] shouldBeEqualTo 'R'.code.toByte()
byteArray[4] shouldBeEqualTo 'I'.code.toByte()
byteArray[5] shouldBeEqualTo 'X'.code.toByte()
// Version
byteArray[6] shouldBeEqualTo 2

View File

@ -51,11 +51,15 @@ interface AuthenticationService {
/**
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
*
* See [LoginWizard] for more details
*/
fun getLoginWizard(): LoginWizard
/**
* Return a RegistrationWizard, to create an matrix account on the homeserver. The login flow has to be retrieved first.
*
* See [RegistrationWizard] for more details.
*/
fun getRegistrationWizard(): RegistrationWizard

View File

@ -17,34 +17,46 @@
package org.matrix.android.sdk.api.auth.login
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.Cancelable
/**
* Set of methods to be able to login to an existing account on a homeserver.
*
* More documentation can be found in the file https://github.com/vector-im/element-android/blob/main/docs/signin.md
*/
interface LoginWizard {
/**
* @param login the login field
* @param password the password field
* Login to the homeserver.
*
* @param login the login field. Can be a user name, or a msisdn (email or phone number) associated to the account
* @param password the password of the account
* @param deviceName the initial device name
* @param callback the matrix callback on which you'll receive the result of authentication.
* @return a [Cancelable]
* @return a [Session] if the login is successful
*/
suspend fun login(login: String,
password: String,
deviceName: String): Session
/**
* Exchange a login token to an access token
* Exchange a login token to an access token.
*
* @param loginToken login token, obtain when login has happen in a WebView, using SSO
* @return a [Session] if the login is successful
*/
suspend fun loginWithToken(loginToken: String): Session
/**
* Reset user password
* Ask the homeserver to reset the user password. The password will not be reset until
* [resetPasswordMailConfirmed] is successfully called.
*
* @param email an email previously associated to the account the user wants the password to be reset.
* @param newPassword the desired new password
*/
suspend fun resetPassword(email: String,
newPassword: String)
/**
* Confirm the new password, once the user has checked their email
* When this method succeed, tha account password will be effectively modified.
*/
suspend fun resetPasswordMailConfirmed()
}

View File

@ -16,32 +16,98 @@
package org.matrix.android.sdk.api.auth.registration
/**
* Set of methods to be able to create an account on a homeserver.
*
* Common scenario to register an account successfully:
* - Call [getRegistrationFlow] to check that you application supports all the mandatory registration stages
* - Call [createAccount] to start the account creation
* - Fulfill all mandatory stages using the methods [performReCaptcha] [acceptTerms] [dummy], etc.
*
* More documentation can be found in the file https://github.com/vector-im/element-android/blob/main/docs/signup.md
* and https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
*/
interface RegistrationWizard {
/**
* Call this method to get the possible registration flow of the current homeserver.
* It can be useful to ensure that your application implementation supports all the stages
* required to create an account. If it is not the case, you will have to use the web fallback
* to let the user create an account with your application.
* See [org.matrix.android.sdk.api.auth.AuthenticationService.getFallbackUrl]
*/
suspend fun getRegistrationFlow(): RegistrationResult
/**
* Can be call to check is the desired userName is available for registration on the current homeserver.
* It may also fails if the desired userName is not correctly formatted or does not follow any restriction on
* the homeserver. Ex: userName with only digits may be rejected.
* @param userName the desired username. Ex: "alice"
*/
suspend fun registrationAvailable(userName: String): RegistrationAvailability
/**
* This is the first method to call in order to create an account and start the registration process.
*
* @param userName the desired username. Ex: "alice"
* @param password the desired password
* @param initialDeviceDisplayName the device display name
*/
suspend fun createAccount(userName: String?,
password: String?,
initialDeviceDisplayName: String?): RegistrationResult
/**
* Perform the "m.login.recaptcha" stage.
*
* @param response the response from ReCaptcha
*/
suspend fun performReCaptcha(response: String): RegistrationResult
/**
* Perform the "m.login.terms" stage.
*/
suspend fun acceptTerms(): RegistrationResult
/**
* Perform the "m.login.dummy" stage.
*/
suspend fun dummy(): RegistrationResult
/**
* Perform the "m.login.email.identity" or "m.login.msisdn" stage.
*
* @param threePid the threePid to add to the account. If this is an email, the homeserver will send an email
* to validate it. For a msisdn a SMS will be sent.
*/
suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult
/**
* Ask the homeserver to send again the current threePid (email or msisdn).
*/
suspend fun sendAgainThreePid(): RegistrationResult
/**
* Send the code received by SMS to validate a msisdn.
* If the code is correct, the registration request will be executed to validate the msisdn.
*/
suspend fun handleValidateThreePid(code: String): RegistrationResult
/**
* Useful to poll the homeserver when waiting for the email to be validated by the user.
* Once the email is validated, this method will return successfully.
* @param delayMillis the SDK can wait before sending the request
*/
suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult
suspend fun registrationAvailable(userName: String): RegistrationAvailability
/**
* This is the current ThreePid, waiting for validation. The SDK will store it in database, so it can be
* restored even if the app has been killed during the registration
*/
val currentThreePid: String?
// True when login and password has been sent with success to the homeserver
/**
* True when login and password have been sent with success to the homeserver, i.e. [createAccount] has been
* called successfully.
*/
val isRegistrationStarted: Boolean
}

View File

@ -66,6 +66,7 @@ interface ProfileService {
/**
* Get the combined profile information for this user.
* This may return keys which are not limited to displayname or avatar_url.
* If server is configured as limit_profile_requests_to_users_who_share_rooms: true then response can be HTTP 403.
* @param userId the userId param to look for
*
*/

View File

@ -83,7 +83,7 @@ interface Room :
* @param beforeLimit how many events before 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 callback Callback to get the search result
* @return The search result
*/
suspend fun search(searchTerm: String,
nextBatch: String?,

View File

@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
* This class holds some data of a room.
* It can be retrieved by [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]
*/
data class RoomSummary constructor(
data class RoomSummary(
val roomId: String,
// Computed display name
val displayName: String = "",
@ -35,6 +35,7 @@ data class RoomSummary constructor(
val avatarUrl: String = "",
val canonicalAlias: String? = null,
val aliases: List<String> = emptyList(),
val joinRules: RoomJoinRules? = null,
val isDirect: Boolean = false,
val directUserId: String? = null,
val joinedMembersCount: Int? = 0,
@ -74,6 +75,9 @@ data class RoomSummary constructor(
val isFavorite: Boolean
get() = hasTag(RoomTag.ROOM_TAG_FAVOURITE)
val isPublic: Boolean
get() = joinRules == RoomJoinRules.PUBLIC
fun hasTag(tag: String) = tags.any { it.name == tag }
val canStartCall: Boolean

View File

@ -29,5 +29,6 @@ data class SpaceChildInfo(
val activeMemberCount: Int?,
val autoJoin: Boolean,
val viaServers: List<String>,
val parentRoomId: String?
val parentRoomId: String?,
val suggested: Boolean?
)

View File

@ -33,7 +33,7 @@ interface Space {
fun spaceSummary(): RoomSummary?
suspend fun addChildren(roomId: String,
viaServers: List<String>,
viaServers: List<String>?,
order: String?,
autoJoin: Boolean = false,
suggested: Boolean? = false)
@ -46,5 +46,8 @@ interface Space {
@Throws
suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean)
@Throws
suspend fun setChildrenSuggested(roomId: String, suggested: Boolean)
// fun getChildren() : List<IRoomSummary>
}

View File

@ -20,6 +20,7 @@ import android.annotation.SuppressLint
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.util.safeCapitalize
/**
* Ref: https://github.com/matrix-org/matrix-doc/issues/1236
@ -39,6 +40,6 @@ data class WidgetContent(
@SuppressLint("DefaultLocale")
fun getHumanName(): String {
return (name ?: type ?: "").capitalize()
return (name ?: type ?: "").safeCapitalize()
}
}

View File

@ -117,22 +117,22 @@ sealed class MatrixItem(
var first = dn[startIndex]
// LEFT-TO-RIGHT MARK
if (dn.length >= 2 && 0x200e == first.toInt()) {
if (dn.length >= 2 && 0x200e == first.code) {
startIndex++
first = dn[startIndex]
}
// check if its the start of a surrogate pair
if (first.toInt() in 0xD800..0xDBFF && dn.length > startIndex + 1) {
if (first.code in 0xD800..0xDBFF && dn.length > startIndex + 1) {
val second = dn[startIndex + 1]
if (second.toInt() in 0xDC00..0xDFFF) {
if (second.code in 0xDC00..0xDFFF) {
length++
}
}
dn.substring(startIndex, startIndex + length)
}
.toUpperCase(Locale.ROOT)
.uppercase(Locale.ROOT)
}
companion object {

View File

@ -545,14 +545,14 @@ internal class DefaultCryptoService @Inject constructor(
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
if (!existingAlgorithm.isNullOrEmpty() && existingAlgorithm != algorithm) {
Timber.e("## CRYPTO | setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
Timber.e("## CRYPTO | setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
return false
}
val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm)
if (!encryptingClass) {
Timber.e("## CRYPTO | setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
Timber.e("## CRYPTO | setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
return false
}
@ -649,17 +649,17 @@ internal class DefaultCryptoService @Inject constructor(
val safeAlgorithm = alg
if (safeAlgorithm != null) {
val t0 = System.currentTimeMillis()
Timber.v("## CRYPTO | encryptEventContent() starts")
Timber.v("## CRYPTO | encryptEventContent() starts")
runCatching {
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
}.foldToCallback(callback)
} else {
val algorithm = getEncryptionAlgorithm(roomId)
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON,
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
Timber.e("## CRYPTO | encryptEventContent() : $reason")
Timber.e("## CRYPTO | encryptEventContent() : $reason")
callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_ENCRYPT, reason)))
}
}
@ -769,7 +769,7 @@ internal class DefaultCryptoService @Inject constructor(
}
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
if (alg == null) {
Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
return
}
alg.onRoomKeyEvent(event, keysBackupService)
@ -777,7 +777,7 @@ internal class DefaultCryptoService @Inject constructor(
private fun onKeyWithHeldReceived(event: Event) {
val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also {
Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
}
Timber.i("## CRYPTO | onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>")
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm)
@ -790,16 +790,16 @@ internal class DefaultCryptoService @Inject constructor(
}
private fun onSecretSendReceived(event: Event) {
Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
if (!event.isEncrypted()) {
// 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")
return
}
// Was that sent by us?
if (event.senderId != userId) {
Timber.e("## CRYPTO | GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
Timber.e("## CRYPTO | GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
return
}
@ -809,13 +809,13 @@ internal class DefaultCryptoService @Inject constructor(
.getOutgoingSecretKeyRequests().firstOrNull { it.requestId == secretContent.requestId }
if (existingRequest == null) {
Timber.i("## CRYPTO | GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
Timber.i("## CRYPTO | GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
return
}
if (!handleSDKLevelGossip(existingRequest.secretName, secretContent.secretValue)) {
// TODO Ask to application layer?
Timber.v("## CRYPTO | onSecretSend() : secret not handled by SDK")
Timber.v("## CRYPTO | onSecretSend() : secret not handled by SDK")
}
}
@ -972,13 +972,13 @@ internal class DefaultCryptoService @Inject constructor(
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
runCatching {
withContext(coroutineDispatchers.crypto) {
Timber.v("## CRYPTO | importRoomKeys starts")
Timber.v("## CRYPTO | importRoomKeys starts")
val t0 = System.currentTimeMillis()
val roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password)
val t1 = System.currentTimeMillis()
Timber.v("## CRYPTO | importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
Timber.v("## CRYPTO | importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
val importedSessions = MoshiProvider.providesMoshi()
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
@ -986,7 +986,7 @@ internal class DefaultCryptoService @Inject constructor(
val t2 = System.currentTimeMillis()
Timber.v("## CRYPTO | importRoomKeys : JSON parsing ${t2 - t1} ms")
Timber.v("## CRYPTO | importRoomKeys : JSON parsing ${t2 - t1} ms")
if (importedSessions == null) {
throw Exception("Error")
@ -1125,7 +1125,7 @@ internal class DefaultCryptoService @Inject constructor(
*/
override fun reRequestRoomKeyForEvent(event: Event) {
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
Timber.e("## CRYPTO | reRequestRoomKeyForEvent Failed to re-request key, null content")
Timber.e("## CRYPTO | reRequestRoomKeyForEvent Failed to re-request key, null content")
}
val requestBody = RoomKeyRequestBody(
@ -1140,18 +1140,18 @@ internal class DefaultCryptoService @Inject constructor(
override fun requestRoomKeyForEvent(event: Event) {
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
Timber.e("## CRYPTO | requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
Timber.e("## CRYPTO | requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
}
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
// if (!isStarted()) {
// Timber.v("## CRYPTO | requestRoomKeyForEvent() : wait after e2e init")
// Timber.v("## CRYPTO | requestRoomKeyForEvent() : wait after e2e init")
// internalStart(false)
// }
roomDecryptorProvider
.getOrCreateRoomDecryptor(event.roomId, wireContent.algorithm)
?.requestKeysForEvent(event, false) ?: run {
Timber.v("## CRYPTO | requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
Timber.v("## CRYPTO | requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
}
}
}
@ -1180,11 +1180,11 @@ internal class DefaultCryptoService @Inject constructor(
// val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
// val now = System.currentTimeMillis()
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
// return
// }
//
// Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
// Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
// lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
//
// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
@ -1201,7 +1201,7 @@ internal class DefaultCryptoService @Inject constructor(
// val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
// val sendToDeviceMap = MXUsersDevicesMap<Any>()
// sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
// Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}")
// Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}")
// val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
// sendToDeviceTask.execute(sendToDeviceParams)
// }
@ -1290,12 +1290,12 @@ internal class DefaultCryptoService @Inject constructor(
override fun prepareToEncrypt(roomId: String, callback: MatrixCallback<Unit>) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
Timber.d("## CRYPTO | prepareToEncrypt() : Check room members up to date")
Timber.d("## CRYPTO | prepareToEncrypt() : Check room members up to date")
// Ensure to load all room members
try {
loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
} catch (failure: Throwable) {
Timber.e("## CRYPTO | prepareToEncrypt() : Failed to load room members")
Timber.e("## CRYPTO | prepareToEncrypt() : Failed to load room members")
callback.onFailure(failure)
return@launch
}
@ -1308,7 +1308,7 @@ internal class DefaultCryptoService @Inject constructor(
if (alg == null) {
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, MXCryptoError.NO_MORE_ALGORITHM_REASON)
Timber.e("## CRYPTO | prepareToEncrypt() : $reason")
Timber.e("## CRYPTO | prepareToEncrypt() : $reason")
callback.onFailure(IllegalArgumentException("Missing algorithm"))
return@launch
}
@ -1318,7 +1318,7 @@ internal class DefaultCryptoService @Inject constructor(
}.fold(
{ callback.onSuccess(Unit) },
{
Timber.e("## CRYPTO | prepareToEncrypt() failed.")
Timber.e("## CRYPTO | prepareToEncrypt() failed.")
callback.onFailure(it)
}
)

View File

@ -111,7 +111,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
res = !notReadyToRetryHS.contains(userId.substringAfter(':'))
}
} catch (e: Exception) {
Timber.e(e, "## CRYPTO | canRetryKeysDownload() failed")
Timber.e(e, "## CRYPTO | canRetryKeysDownload() failed")
}
}
@ -150,7 +150,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
for (userId in userIds) {
if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) {
Timber.v("## CRYPTO | startTrackingDeviceList() : Now tracking device list for $userId")
Timber.v("## CRYPTO | startTrackingDeviceList() : Now tracking device list for $userId")
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
isUpdated = true
}
@ -178,7 +178,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
for (userId in changed) {
if (deviceTrackingStatuses.containsKey(userId)) {
Timber.v("## CRYPTO | handleDeviceListsChanges() : Marking device list outdated for $userId")
Timber.v("## CRYPTO | handleDeviceListsChanges() : Marking device list outdated for $userId")
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
isUpdated = true
}
@ -186,7 +186,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
for (userId in left) {
if (deviceTrackingStatuses.containsKey(userId)) {
Timber.v("## CRYPTO | handleDeviceListsChanges() : No longer tracking device list for $userId")
Timber.v("## CRYPTO | handleDeviceListsChanges() : No longer tracking device list for $userId")
deviceTrackingStatuses[userId] = TRACKING_STATUS_NOT_TRACKED
isUpdated = true
}
@ -276,7 +276,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
* @param forceDownload Always download the keys even if cached.
*/
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): MXUsersDevicesMap<CryptoDeviceInfo> {
Timber.v("## CRYPTO | downloadKeys() : forceDownload $forceDownload : $userIds")
Timber.v("## CRYPTO | downloadKeys() : forceDownload $forceDownload : $userIds")
// Map from userId -> deviceId -> MXDeviceInfo
val stored = MXUsersDevicesMap<CryptoDeviceInfo>()
@ -305,13 +305,13 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
}
}
return if (downloadUsers.isEmpty()) {
Timber.v("## CRYPTO | downloadKeys() : no new user device")
Timber.v("## CRYPTO | downloadKeys() : no new user device")
stored
} else {
Timber.v("## CRYPTO | downloadKeys() : starts")
Timber.v("## CRYPTO | downloadKeys() : starts")
val t0 = System.currentTimeMillis()
val result = doKeyDownloadForUsers(downloadUsers)
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
result.also {
it.addEntriesFromMap(stored)
}
@ -324,7 +324,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
* @param downloadUsers the user ids list
*/
private suspend fun doKeyDownloadForUsers(downloadUsers: List<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
Timber.v("## CRYPTO | doKeyDownloadForUsers() : doKeyDownloadForUsers ${downloadUsers.logLimit()}")
Timber.v("## CRYPTO | doKeyDownloadForUsers() : doKeyDownloadForUsers ${downloadUsers.logLimit()}")
// get the user ids which did not already trigger a keys download
val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) }
if (filteredUsers.isEmpty()) {
@ -335,16 +335,16 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
val response = try {
downloadKeysForUsersTask.execute(params)
} catch (throwable: Throwable) {
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
onKeysDownloadFailed(filteredUsers)
throw throwable
}
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
for (userId in filteredUsers) {
// al devices =
val models = response.deviceKeys?.get(userId)?.mapValues { entry -> CryptoInfoMapper.map(entry.value) }
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for $userId : $models")
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for $userId : $models")
if (!models.isNullOrEmpty()) {
val workingCopy = models.toMutableMap()
for ((deviceId, deviceInfo) in models) {
@ -377,13 +377,13 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
}
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
}
val selfSigningKey = response.selfSigningKeys?.get(userId)?.toCryptoModel()?.also {
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : SSK ${it.unpaddedBase64PublicKey}")
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : SSK ${it.unpaddedBase64PublicKey}")
}
val userSigningKey = response.userSigningKeys?.get(userId)?.toCryptoModel()?.also {
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : USK ${it.unpaddedBase64PublicKey}")
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : USK ${it.unpaddedBase64PublicKey}")
}
cryptoStore.storeUserCrossSigningKeys(
userId,
@ -411,28 +411,28 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
*/
private fun validateDeviceKeys(deviceKeys: CryptoDeviceInfo?, userId: String, deviceId: String, previouslyStoredDeviceKeys: CryptoDeviceInfo?): Boolean {
if (null == deviceKeys) {
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys is null from $userId:$deviceId")
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys is null from $userId:$deviceId")
return false
}
if (null == deviceKeys.keys) {
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.keys is null from $userId:$deviceId")
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.keys is null from $userId:$deviceId")
return false
}
if (null == deviceKeys.signatures) {
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.signatures is null from $userId:$deviceId")
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.signatures is null from $userId:$deviceId")
return false
}
// Check that the user_id and device_id in the received deviceKeys are correct
if (deviceKeys.userId != userId) {
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched user_id ${deviceKeys.userId} from $userId:$deviceId")
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched user_id ${deviceKeys.userId} from $userId:$deviceId")
return false
}
if (deviceKeys.deviceId != deviceId) {
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched device_id ${deviceKeys.deviceId} from $userId:$deviceId")
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched device_id ${deviceKeys.deviceId} from $userId:$deviceId")
return false
}
@ -440,21 +440,21 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
val signKey = deviceKeys.keys[signKeyId]
if (null == signKey) {
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no ed25519 key")
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no ed25519 key")
return false
}
val signatureMap = deviceKeys.signatures[userId]
if (null == signatureMap) {
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no map for $userId")
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no map for $userId")
return false
}
val signature = signatureMap[signKeyId]
if (null == signature) {
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} is not signed")
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} is not signed")
return false
}
@ -469,7 +469,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
}
if (!isVerified) {
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":"
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":"
+ deviceKeys.deviceId + " with error " + errorMessage)
return false
}
@ -480,12 +480,12 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
// best off sticking with the original keys.
//
// Should we warn the user about it somehow?
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":"
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":"
+ deviceKeys.deviceId + " has changed : "
+ previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
return false
}
@ -499,7 +499,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
* This method must be called on getEncryptingThreadHandler() thread.
*/
suspend fun refreshOutdatedDeviceLists() {
Timber.v("## CRYPTO | refreshOutdatedDeviceLists()")
Timber.v("## CRYPTO | refreshOutdatedDeviceLists()")
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
val users = deviceTrackingStatuses.keys.filterTo(mutableListOf()) { userId ->
@ -518,10 +518,10 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
doKeyDownloadForUsers(users)
}.fold(
{
Timber.v("## CRYPTO | refreshOutdatedDeviceLists() : done")
Timber.v("## CRYPTO | refreshOutdatedDeviceLists() : done")
},
{
Timber.e(it, "## CRYPTO | refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
Timber.e(it, "## CRYPTO | refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
}
)
}

View File

@ -92,20 +92,20 @@ internal class EventDecryptor @Inject constructor(
private fun internalDecryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
val eventContent = event.content
if (eventContent == null) {
Timber.e("## CRYPTO | decryptEvent : empty event content")
Timber.e("## CRYPTO | decryptEvent : empty event content")
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
} else {
val algorithm = eventContent["algorithm"]?.toString()
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
if (alg == null) {
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm)
Timber.e("## CRYPTO | decryptEvent() : $reason")
Timber.e("## CRYPTO | decryptEvent() : $reason")
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
} else {
try {
return alg.decryptEvent(event, timeline)
} catch (mxCryptoError: MXCryptoError) {
Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError")
Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError")
if (algorithm == MXCRYPTO_ALGORITHM_OLM) {
if (mxCryptoError is MXCryptoError.Base
&& mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) {
@ -119,7 +119,7 @@ internal class EventDecryptor @Inject constructor(
markOlmSessionForUnwedging(event.senderId ?: "", it)
}
?: run {
Timber.i("## CRYPTO | internalDecryptEvent() : Failed to find sender crypto device for unwedging")
Timber.i("## CRYPTO | internalDecryptEvent() : Failed to find sender crypto device for unwedging")
}
}
}
@ -137,18 +137,18 @@ internal class EventDecryptor @Inject constructor(
val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
val now = System.currentTimeMillis()
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
Timber.w("## 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
}
Timber.i("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
Timber.i("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
// offload this from crypto thread (?)
cryptoCoroutineScope.launch(coroutineDispatchers.computation) {
val ensured = ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true)
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : ensureOlmSessionsForDevicesAction isEmpty:${ensured.isEmpty}")
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : ensureOlmSessionsForDevicesAction isEmpty:${ensured.isEmpty}")
// 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)
@ -161,13 +161,13 @@ internal class EventDecryptor @Inject constructor(
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : sending dummy to $senderId:${deviceInfo.deviceId}")
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : sending dummy to $senderId:${deviceInfo.deviceId}")
withContext(coroutineDispatchers.io) {
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
try {
sendToDeviceTask.execute(sendToDeviceParams)
} catch (failure: Throwable) {
Timber.e(failure, "## CRYPTO | markOlmSessionForUnwedging() : failed to send dummy to $senderId:${deviceInfo.deviceId}")
Timber.e(failure, "## CRYPTO | markOlmSessionForUnwedging() : failed to send dummy to $senderId:${deviceInfo.deviceId}")
}
}
}

View File

@ -74,7 +74,7 @@ internal class MXMegolmDecryption(private val userId: String,
@Throws(MXCryptoError::class)
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult {
Timber.v("## CRYPTO | decryptEvent ${event.eventId} , requestKeysOnFail:$requestKeysOnFail")
Timber.v("## CRYPTO | decryptEvent ${event.eventId}, requestKeysOnFail:$requestKeysOnFail")
if (event.roomId.isNullOrBlank()) {
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
}
@ -360,7 +360,7 @@ internal class MXMegolmDecryption(private val userId: String,
},
{
// TODO
Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body")
Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body")
}
)

View File

@ -80,9 +80,9 @@ internal class MXMegolmEncryption(
eventType: String,
userIds: List<String>): Content {
val ts = System.currentTimeMillis()
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
val devices = getDevicesInRoom(userIds)
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
val outboundSession = ensureOutboundSession(devices.allowedDevices)
return encryptContent(outboundSession, eventType, eventContent)
@ -91,7 +91,7 @@ internal class MXMegolmEncryption(
// annoyingly we have to serialize again the saved outbound session to store message index :/
// if not we would see duplicate message index errors
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis")
Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis")
}
}
@ -118,13 +118,13 @@ internal class MXMegolmEncryption(
override suspend fun preshareKey(userIds: List<String>) {
val ts = System.currentTimeMillis()
Timber.v("## CRYPTO | preshareKey : getDevicesInRoom")
Timber.v("## CRYPTO | preshareKey : getDevicesInRoom")
val devices = getDevicesInRoom(userIds)
val outboundSession = ensureOutboundSession(devices.allowedDevices)
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
Timber.v("## CRYPTO | preshareKey ${System.currentTimeMillis() - ts} millis")
Timber.v("## CRYPTO | preshareKey ${System.currentTimeMillis() - ts} millis")
}
/**
@ -133,7 +133,7 @@ internal class MXMegolmEncryption(
* @return the session description
*/
private fun prepareNewSessionInRoom(): MXOutboundSessionInfo {
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
val sessionId = olmDevice.createOutboundGroupSessionForRoom(roomId)
val keysClaimedMap = HashMap<String, String>()
@ -153,7 +153,7 @@ internal class MXMegolmEncryption(
* @param devicesInRoom the devices list
*/
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
Timber.v("## CRYPTO | ensureOutboundSession start")
Timber.v("## CRYPTO | ensureOutboundSession start")
var session = outboundSession
if (session == null
// Need to make a brand new session?
@ -190,7 +190,7 @@ internal class MXMegolmEncryption(
devicesByUsers: Map<String, List<CryptoDeviceInfo>>) {
// nothing to send, the task is done
if (devicesByUsers.isEmpty()) {
Timber.v("## CRYPTO | shareKey() : nothing more to do")
Timber.v("## CRYPTO | shareKey() : nothing more to do")
return
}
// reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user)
@ -203,7 +203,7 @@ internal class MXMegolmEncryption(
break
}
}
Timber.v("## CRYPTO | shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
Timber.v("## CRYPTO | shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
shareUserDevicesKey(session, subMap)
val remainingDevices = devicesByUsers - subMap.keys
shareKey(session, remainingDevices)
@ -232,11 +232,11 @@ internal class MXMegolmEncryption(
payload["content"] = submap
var t0 = System.currentTimeMillis()
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
Timber.v(
"""## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
"""## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
.trimMargin()
)
val contentMap = MXUsersDevicesMap<Any>()
@ -257,7 +257,7 @@ internal class MXMegolmEncryption(
noOlmToNotify.add(UserDevice(userId, deviceID))
continue
}
Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo)))
haveTargets = true
}
@ -289,17 +289,17 @@ internal class MXMegolmEncryption(
if (haveTargets) {
t0 = System.currentTimeMillis()
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
try {
sendToDeviceTask.execute(sendToDeviceParams)
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
} catch (failure: Throwable) {
// 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 {
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
}
if (noOlmToNotify.isNotEmpty()) {
@ -317,7 +317,7 @@ internal class MXMegolmEncryption(
sessionId: String,
senderKey: String?,
code: WithHeldCode) {
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code")
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code")
val withHeldContent = RoomKeyWithHeldContent(
roomId = roomId,
senderKey = senderKey,
@ -336,7 +336,7 @@ internal class MXMegolmEncryption(
try {
sendToDeviceTask.execute(params)
} catch (failure: Throwable) {
Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ")
Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ")
}
}
@ -473,7 +473,7 @@ internal class MXMegolmEncryption(
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId")
Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
return try {
sendToDeviceTask.execute(sendToDeviceParams)

View File

@ -57,7 +57,7 @@ object HkdfSha256 {
/*
The output OKM is calculated as follows:
Notation | -> When the message is composed of several elements we use concatenation (denoted |) in the second argument;
Notation | -> When the message is composed of several elements we use concatenation (denoted |) in the second argument;
N = ceil(L/HashLen)

View File

@ -345,7 +345,7 @@ internal abstract class SASDefaultVerificationTransaction(
}
protected fun hashUsingAgreedHashMethod(toHash: String): String? {
if ("sha256" == accepted?.hash?.toLowerCase(Locale.ROOT)) {
if ("sha256" == accepted?.hash?.lowercase(Locale.ROOT)) {
val olmUtil = OlmUtility()
val hashBytes = olmUtil.sha256(toHash)
olmUtil.releaseUtility()
@ -355,7 +355,7 @@ internal abstract class SASDefaultVerificationTransaction(
}
private fun macUsingAgreedMethod(message: String, info: String): String? {
return when (accepted?.messageAuthenticationCode?.toLowerCase(Locale.ROOT)) {
return when (accepted?.messageAuthenticationCode?.lowercase(Locale.ROOT)) {
SAS_MAC_SHA256_LONGKDF -> getSAS().calculateMacLongKdf(message, info)
SAS_MAC_SHA256 -> getSAS().calculateMac(message, info)
else -> null

View File

@ -48,7 +48,7 @@ fun QrCodeData.toEncodedString(): String {
// TransactionId
transactionId.forEach {
result += it.toByte()
result += it.code.toByte()
}
// Keys

View File

@ -20,6 +20,7 @@ import io.realm.DynamicRealm
import io.realm.FieldAttribute
import io.realm.RealmMigration
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields
@ -33,9 +34,9 @@ import org.matrix.android.sdk.internal.database.model.RoomEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomTagEntityFields
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.di.MoshiProvider
import timber.log.Timber
import javax.inject.Inject
@ -43,7 +44,7 @@ import javax.inject.Inject
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
companion object {
const val SESSION_STORE_SCHEMA_VERSION = 11L
const val SESSION_STORE_SCHEMA_VERSION = 13L
}
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@ -60,6 +61,8 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
if (oldVersion <= 8) migrateTo9(realm)
if (oldVersion <= 9) migrateTo10(realm)
if (oldVersion <= 10) migrateTo11(realm)
if (oldVersion <= 11) migrateTo12(realm)
if (oldVersion <= 12) migrateTo13(realm)
}
private fun migrateTo1(realm: DynamicRealm) {
@ -144,10 +147,6 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
Timber.d("Step 7 -> 8")
val editionOfEventSchema = realm.schema.create("EditionOfEvent")
.apply {
// setEmbedded does not return `this`...
isEmbedded = true
}
.addField(EditionOfEventFields.CONTENT, String::class.java)
.addField(EditionOfEventFields.EVENT_ID, String::class.java)
.setRequired(EditionOfEventFields.EVENT_ID, true)
@ -162,6 +161,10 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
?.removeField("lastEditTs")
?.removeField("sourceLocalEchoEvents")
?.addRealmListField(EditAggregatedSummaryEntityFields.EDITIONS.`$`, editionOfEventSchema)
// This has to be done once a parent use the model as a child
// See https://github.com/realm/realm-java/issues/7402
editionOfEventSchema.isEmbedded = true
}
private fun migrateTo9(realm: DynamicRealm) {
@ -247,4 +250,39 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
realm.schema.get("EventEntity")
?.addField(EventEntityFields.SEND_STATE_DETAILS, String::class.java)
}
private fun migrateTo12(realm: DynamicRealm) {
Timber.d("Step 11 -> 12")
val joinRulesContentAdapter = MoshiProvider.providesMoshi().adapter(RoomJoinRulesContent::class.java)
realm.schema.get("RoomSummaryEntity")
?.addField(RoomSummaryEntityFields.JOIN_RULES_STR, String::class.java)
?.transform { obj ->
val joinRulesEvent = realm.where("CurrentStateEventEntity")
.equalTo(CurrentStateEventEntityFields.ROOM_ID, obj.getString(RoomSummaryEntityFields.ROOM_ID))
.equalTo(CurrentStateEventEntityFields.TYPE, EventType.STATE_ROOM_JOIN_RULES)
.findFirst()
val roomJoinRules = joinRulesEvent?.getObject(CurrentStateEventEntityFields.ROOT.`$`)
?.getString(EventEntityFields.CONTENT)?.let {
joinRulesContentAdapter.fromJson(it)?.joinRules
}
obj.setString(RoomSummaryEntityFields.JOIN_RULES_STR, roomJoinRules?.name)
}
realm.schema.get("SpaceChildSummaryEntity")
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
}
private fun migrateTo13(realm: DynamicRealm) {
Timber.d("Step 12 -> 13")
// Fix issue with the nightly build. Eventually play again the migration which has been included in migrateTo12()
realm.schema.get("SpaceChildSummaryEntity")
?.takeIf { !it.hasField(SpaceChildSummaryEntityFields.SUGGESTED) }
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
}
}

View File

@ -44,6 +44,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
name = roomSummaryEntity.name ?: "",
topic = roomSummaryEntity.topic ?: "",
avatarUrl = roomSummaryEntity.avatarUrl ?: "",
joinRules = roomSummaryEntity.joinRules,
isDirect = roomSummaryEntity.isDirect,
directUserId = roomSummaryEntity.directUserId,
latestPreviewableEvent = latestEvent,
@ -88,7 +89,8 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
order = it.order,
autoJoin = it.autoJoin ?: false,
viaServers = it.viaServers.toList(),
parentRoomId = roomSummaryEntity.roomId
parentRoomId = roomSummaryEntity.roomId,
suggested = it.suggested
)
},
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList()

View File

@ -21,7 +21,9 @@ import io.realm.RealmObject
import io.realm.annotations.Index
import io.realm.annotations.PrimaryKey
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.VersioningState
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
@ -242,6 +244,19 @@ internal open class RoomSummaryEntity(
}
}
private var joinRulesStr: String? = null
var joinRules: RoomJoinRules?
get() {
return joinRulesStr?.let {
tryOrNull { RoomJoinRules.valueOf(it) }
}
}
set(value) {
if (value?.name != joinRulesStr) {
joinRulesStr = value?.name
}
}
var roomEncryptionTrustLevel: RoomEncryptionTrustLevel?
get() {
return roomEncryptionTrustLevelStr?.let {

View File

@ -29,6 +29,8 @@ internal open class SpaceChildSummaryEntity(
var autoJoin: Boolean? = null,
var suggested: Boolean? = null,
var childRoomId: String? = null,
// Link to the actual space summary if it is known locally
var childSummaryEntity: RoomSummaryEntity? = null,

View File

@ -291,7 +291,7 @@ internal class DefaultFileService @Inject constructor(
Timber.v("Get size of ${it.absolutePath}")
true
}
.sumBy { it.length().toInt() }
.sumOf { it.length().toInt() }
}
override fun clearCache() {

View File

@ -117,7 +117,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
return withOlmUtility { olmUtility ->
threePids.map { threePid ->
base64ToBase64Url(
olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT)
olmUtility.sha256(threePid.value.lowercase(Locale.ROOT)
+ " " + threePid.toMedium() + " " + pepper)
)
}

View File

@ -18,19 +18,13 @@ package org.matrix.android.sdk.internal.session.permalinks
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter
import java.net.URLEncoder
import javax.inject.Inject
import javax.inject.Provider
internal class PermalinkFactory @Inject constructor(
@UserId
private val userId: String,
// Use a provider to fix circular Dagger dependency
private val roomGetterProvider: Provider<RoomGetter>
private val viaParameterFinder: ViaParameterFinder
) {
fun createPermalink(event: Event): String? {
@ -50,12 +44,12 @@ internal class PermalinkFactory @Inject constructor(
return if (roomId.isEmpty()) {
null
} else {
MATRIX_TO_URL_BASE + escape(roomId) + computeViaParams(userId, roomId)
MATRIX_TO_URL_BASE + escape(roomId) + viaParameterFinder.computeViaParams(userId, roomId)
}
}
fun createPermalink(roomId: String, eventId: String): String {
return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId) + computeViaParams(userId, roomId)
return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId) + viaParameterFinder.computeViaParams(userId, roomId)
}
fun getLinkedId(url: String): String? {
@ -66,25 +60,6 @@ internal class PermalinkFactory @Inject constructor(
} else null
}
/**
* Compute the via parameters.
* Take up to 3 homeserver domains, taking the most representative one regarding room members and including the
* current user one.
*/
private fun computeViaParams(userId: String, roomId: String): String {
val userHomeserver = userId.substringAfter(":")
return getUserIdsOfJoinedMembers(roomId)
.map { it.substringAfter(":") }
.groupBy { it }
.mapValues { it.value.size }
.toMutableMap()
// Ensure the user homeserver will be included
.apply { this[userHomeserver] = Int.MAX_VALUE }
.let { map -> map.keys.sortedByDescending { map[it] } }
.take(3)
.joinToString(prefix = "?via=", separator = "&via=") { URLEncoder.encode(it, "utf-8") }
}
/**
* Escape '/' in id, because it is used as a separator
*
@ -104,15 +79,4 @@ internal class PermalinkFactory @Inject constructor(
private fun unescape(id: String): String {
return id.replace("%2F", "/")
}
/**
* Get a set of userIds of joined members of a room
*/
private fun getUserIdsOfJoinedMembers(roomId: String): Set<String> {
return roomGetterProvider.get().getRoom(roomId)
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
?.map { it.userId }
.orEmpty()
.toSet()
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2021 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.permalinks
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter
import java.net.URLEncoder
import javax.inject.Inject
import javax.inject.Provider
internal class ViaParameterFinder @Inject constructor(
@UserId private val userId: String,
private val roomGetterProvider: Provider<RoomGetter>
) {
fun computeViaParams(roomId: String, max: Int): List<String> {
return computeViaParams(userId, roomId, max)
}
/**
* Compute the via parameters.
* Take up to 3 homeserver domains, taking the most representative one regarding room members and including the
* current user one.
*/
fun computeViaParams(userId: String, roomId: String): String {
return computeViaParams(userId, roomId, 3)
.joinToString(prefix = "?via=", separator = "&via=") { URLEncoder.encode(it, "utf-8") }
}
fun computeViaParams(userId: String, roomId: String, max: Int): List<String> {
val userHomeserver = userId.substringAfter(":")
return getUserIdsOfJoinedMembers(roomId)
.map { it.substringAfter(":") }
.groupBy { it }
.mapValues { it.value.size }
.toMutableMap()
// Ensure the user homeserver will be included
.apply { this[userHomeserver] = Int.MAX_VALUE }
.let { map -> map.keys.sortedByDescending { map[it] } }
.take(max)
}
/**
* Get a set of userIds of joined members of a room
*/
private fun getUserIdsOfJoinedMembers(roomId: String): Set<String> {
return roomGetterProvider.get().getRoom(roomId)
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
?.map { it.userId }
.orEmpty()
.toSet()
}
}

View File

@ -33,6 +33,7 @@ internal interface ProfileAPI {
* Get the combined profile information for this user.
* This API may be used to fetch the user's own profile information or other users; either locally or on remote homeservers.
* This API may return keys which are not limited to displayname or avatar_url.
* If server is configured as limit_profile_requests_to_users_who_share_rooms: true then response can be HTTP 403.
* @param userId the user id to fetch profile info
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}")

View File

@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.api.session.space.Space
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
import org.matrix.android.sdk.internal.session.search.SearchTask
@ -66,6 +67,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
private val roomMembersService: MembershipService,
private val roomPushRuleService: RoomPushRuleService,
private val sendStateTask: SendStateTask,
private val viaParameterFinder: ViaParameterFinder,
private val searchTask: SearchTask) :
Room,
TimelineService by timelineService,
@ -154,6 +156,6 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
override fun asSpace(): Space? {
if (roomSummary()?.roomType != RoomType.SPACE) return null
return DefaultSpace(this, roomSummaryDataSource)
return DefaultSpace(this, roomSummaryDataSource, viaParameterFinder)
}
}

View File

@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
import org.matrix.android.sdk.internal.session.room.alias.DefaultAliasService
import org.matrix.android.sdk.internal.session.room.call.DefaultRoomCallService
import org.matrix.android.sdk.internal.session.room.draft.DefaultDraftService
@ -60,6 +61,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
private val membershipServiceFactory: DefaultMembershipService.Factory,
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory,
private val sendStateTask: SendStateTask,
private val viaParameterFinder: ViaParameterFinder,
private val searchTask: SearchTask) :
RoomFactory {
@ -83,7 +85,8 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
roomMembersService = membershipServiceFactory.create(roomId),
roomPushRuleService = roomPushRuleServiceFactory.create(roomId),
sendStateTask = sendStateTask,
searchTask = searchTask
searchTask = searchTask,
viaParameterFinder = viaParameterFinder
)
}
}

View File

@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
import org.matrix.android.sdk.api.session.room.model.RoomNameContent
import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
import org.matrix.android.sdk.api.session.room.model.RoomType
@ -104,6 +105,7 @@ internal class RoomSummaryUpdater @Inject constructor(
val lastCanonicalAliasEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
val lastAliasesEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ALIASES, stateKey = "")?.root
val roomCreateEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CREATE, stateKey = "")?.root
val joinRulesEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_JOIN_RULES, stateKey = "")?.root
val roomType = ContentMapper.map(roomCreateEvent?.content).toModel<RoomCreateContent>()?.type
roomSummaryEntity.roomType = roomType
@ -130,6 +132,7 @@ internal class RoomSummaryUpdater @Inject constructor(
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId)
roomSummaryEntity.name = ContentMapper.map(lastNameEvent?.content).toModel<RoomNameContent>()?.name
roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel<RoomTopicContent>()?.topic
roomSummaryEntity.joinRules = ContentMapper.map(joinRulesEvent?.content).toModel<RoomJoinRulesContent>()?.joinRules
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
roomSummaryEntity.canonicalAlias = ContentMapper.map(lastCanonicalAliasEvent?.content).toModel<RoomCanonicalAliasContent>()
?.canonicalAlias
@ -361,6 +364,8 @@ internal class RoomSummaryUpdater @Inject constructor(
realm.where(RoomSummaryEntity::class.java)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, listOf(Membership.JOIN))
.notEqualTo(RoomSummaryEntityFields.ROOM_TYPE, RoomType.SPACE)
// also we do not count DM in here, because home space will already show them
.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
.contains(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, space.roomId)
.findAll().forEach {
highlightCount += it.highlightCount

View File

@ -24,11 +24,13 @@ import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.space.Space
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
internal class DefaultSpace(
private val room: Room,
private val spaceSummaryDataSource: RoomSummaryDataSource
private val spaceSummaryDataSource: RoomSummaryDataSource,
private val viaParameterFinder: ViaParameterFinder
) : Space {
override fun asRoom(): Room {
@ -46,15 +48,17 @@ internal class DefaultSpace(
}
override suspend fun addChildren(roomId: String,
viaServers: List<String>,
viaServers: List<String>?,
order: String?,
autoJoin: Boolean,
suggested: Boolean?) {
// Find best via
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
stateKey = roomId,
body = SpaceChildContent(
via = viaServers,
via = viaServers ?: viaParameterFinder.computeViaParams(roomId, 3),
autoJoin = autoJoin,
order = order,
suggested = suggested
@ -63,20 +67,21 @@ internal class DefaultSpace(
}
override suspend fun removeChildren(roomId: String) {
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
.firstOrNull()
?.content.toModel<SpaceChildContent>()
?: // should we throw here?
return
// val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
// .firstOrNull()
// ?.content.toModel<SpaceChildContent>()
// ?: // should we throw here?
// return
// edit state event and set via to null
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
stateKey = roomId,
body = SpaceChildContent(
order = existing.order,
order = null,
via = null,
autoJoin = existing.autoJoin
autoJoin = null,
suggested = null
).toContent()
)
}
@ -94,7 +99,8 @@ internal class DefaultSpace(
body = SpaceChildContent(
order = order,
via = existing.via,
autoJoin = existing.autoJoin
autoJoin = existing.autoJoin,
suggested = existing.suggested
).toContent()
)
}
@ -105,6 +111,11 @@ internal class DefaultSpace(
?.content.toModel<SpaceChildContent>()
?: throw IllegalArgumentException("$roomId is not a child of this space")
if (existing.autoJoin == autoJoin) {
// nothing to do?
return
}
// edit state event and set via to null
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
@ -112,7 +123,31 @@ internal class DefaultSpace(
body = SpaceChildContent(
order = existing.order,
via = existing.via,
autoJoin = autoJoin
autoJoin = autoJoin,
suggested = existing.suggested
).toContent()
)
}
override suspend fun setChildrenSuggested(roomId: String, suggested: Boolean) {
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
.firstOrNull()
?.content.toModel<SpaceChildContent>()
?: throw IllegalArgumentException("$roomId is not a child of this space")
if (existing.suggested == suggested) {
// nothing to do?
return
}
// edit state event and set via to null
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
stateKey = roomId,
body = SpaceChildContent(
order = existing.order,
via = existing.via,
autoJoin = existing.autoJoin,
suggested = suggested
).toContent()
)
}

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