Merge tag 'v1.1.7' into sc
Change-Id: Icefe721894c7cd3d0702d07efbd0452f95775d32 Conflicts: .idea/codeStyles/Project.xml matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt vector/src/main/res/layout/item_timeline_event_base.xml vector/src/main/res/values-ca/strings.xml vector/src/main/res/values-cs/strings.xml vector/src/main/res/values-de/strings.xml vector/src/main/res/values-et/strings.xml vector/src/main/res/values-fa/strings.xml vector/src/main/res/values-fr/strings.xml vector/src/main/res/values-it/strings.xml vector/src/main/res/values-sq/strings.xml vector/src/main/res/values-sv/strings.xml vector/src/main/res/values-vi/strings.xml vector/src/main/res/values-zh-rTW/strings.xml vector/src/main/res/values/strings.xml vector/src/main/res/xml/vector_settings_labs.xml
This commit is contained in:
commit
db6cfd3054
|
@ -4,7 +4,7 @@
|
|||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
|
||||
|
@ -153,4 +153,4 @@
|
|||
<option name="FIELD_ANNOTATION_WRAP" value="1" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
</component>
|
||||
|
|
20
CHANGES.md
20
CHANGES.md
|
@ -1,4 +1,4 @@
|
|||
Changes in Element 1.1.7 (2021-XX-XX)
|
||||
Changes in Element 1.1.7 (2021-05-12)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
|
@ -7,6 +7,11 @@ Features ✨:
|
|||
Improvements 🙌:
|
||||
- Add ability to install APK from directly from Element (#2381)
|
||||
- Delete and react to stickers (#3250)
|
||||
- Compress video before sending (#442)
|
||||
- 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)
|
||||
|
@ -17,18 +22,19 @@ Bugfix 🐛:
|
|||
- Fix issue when opening encrypted files (#3186)
|
||||
- Fix wording issue (#3242)
|
||||
- Fix missing sender information after edits (#3184)
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
- 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
|
||||
- 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)
|
||||
|
||||
Build 🧱:
|
||||
- Upgrade to gradle 7
|
||||
|
||||
Test:
|
||||
-
|
||||
- https://github.com/Piasy/BigImageViewer is now hosted on mavenCentral()
|
||||
- Upgrade Realm to version 10.4.0
|
||||
|
||||
Other changes:
|
||||
- New store descriptions
|
||||
|
|
|
@ -17,20 +17,6 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
content {
|
||||
// PhotoView
|
||||
includeGroupByRegex 'com\\.github\\.chrisbanes'
|
||||
}
|
||||
}
|
||||
jcenter()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
|
|
|
@ -45,17 +45,11 @@ allprojects {
|
|||
// PFLockScreen-Android
|
||||
includeGroupByRegex 'com\\.github\\.vector-im'
|
||||
|
||||
//Chat effects
|
||||
// Chat effects
|
||||
includeGroupByRegex 'com\\.github\\.jetradarmobile'
|
||||
includeGroupByRegex 'nl\\.dionsegijn'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://dl.bintray.com/piasy/maven"
|
||||
content {
|
||||
includeGroupByRegex "com\\.github\\.piasy"
|
||||
}
|
||||
}
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||
// Jitsi repo
|
||||
maven {
|
||||
|
@ -64,6 +58,7 @@ allprojects {
|
|||
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.1.0"
|
||||
}
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1 +1 @@
|
|||
Zabezpečený decentralizovaný chat a VoIP. Uchovejte svá data v bezpečí.
|
||||
Skupinový messenger - šifrovaná komunikace, skupinový chat a video hovory
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (dříve Riot.im)
|
||||
Element - bezpečný messenger
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Sicherer dezentraler Chat und Telefonie. Schütze deine Daten vor Dritten.
|
||||
Gruppen-Messenger - verschlüsselte Kommunikation, Gruppenchat und Videoanrufe
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (zuvor Riot.im)
|
||||
Element - Sicherer Messenger
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (varem Riot.im)
|
||||
Element - turvaline sõnumiklient
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (anciennement Riot.im)
|
||||
Element - Messagerie sécurisée
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
Haadferoaring yn disse ferzje: Breksoplossings!
|
||||
Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.0.17
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
Groepsberjochtetsjinst - fersifere berjochten, groeps petearen en fideo skilje
|
|
@ -0,0 +1 @@
|
|||
Element - Feilige Berjochtetsjinst
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (ex Riot.im)
|
||||
Element - Messaggi sicuri
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Säker decentraliserad chatt och VoIP. Håll din data säker från tredje parter.
|
||||
Gruppmeddelandeapp - krypterade meddelanden, gruppchatt och videosamtal
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (före detta Riot.im)
|
||||
Element - Säker meddelandeapp
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:效能改善與錯誤修復!
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.1.4
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:1.1.4 的快速修補
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.1.5
|
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:1.1.5 的快速修補
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.1.6
|
|
@ -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 間完全同步的訊息歷史保持聯絡
|
||||
|
|
|
@ -1 +1 @@
|
|||
安全的去中心化聊天與 VoIP。確保您的資料不受第三方的影響。
|
||||
群組通訊軟體 - 訊息加密、群組聊天與視訊通話
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element(曾名為 Riot.im)
|
||||
Element - 安全的通訊軟體
|
||||
|
|
|
@ -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.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,6 +165,9 @@ dependencies {
|
|||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
|
||||
|
||||
// Video compression
|
||||
implementation 'com.otaliastudios:transcoder:0.10.3'
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.22'
|
||||
|
||||
|
@ -187,7 +187,7 @@ dependencies {
|
|||
androidTestImplementation 'androidx.test:rules:1.3.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
androidTestImplementation 'org.amshove.kluent:kluent-android:1.61'
|
||||
androidTestImplementation 'org.amshove.kluent:kluent-android:1.65'
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.11.0'
|
||||
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.crypto
|
|||
|
||||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -29,8 +31,6 @@ import org.matrix.android.sdk.common.CommonTestHelper
|
|||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
|
@ -54,7 +54,7 @@ class PreShareKeysTest : InstrumentedTest {
|
|||
&& it.getClearType() == EventType.ROOM_KEY
|
||||
}
|
||||
|
||||
assertEquals(0, preShareCount, "Bob should not have receive any key from alice at this point")
|
||||
assertEquals("Bob should not have receive any key from alice at this point", 0, preShareCount)
|
||||
Log.d("#Test", "Room Key Received from alice $preShareCount")
|
||||
|
||||
// Force presharing of new outbound key
|
||||
|
@ -78,14 +78,14 @@ class PreShareKeysTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
val content = latest?.getClearContent().toModel<RoomKeyContent>()
|
||||
assertNotNull(content, "Bob should have received and decrypted a room key event from alice")
|
||||
assertEquals(e2eRoomID, content.roomId, "Wrong room")
|
||||
assertNotNull("Bob should have received and decrypted a room key event from alice", content)
|
||||
assertEquals("Wrong room", e2eRoomID, content!!.roomId)
|
||||
val megolmSessionId = content.sessionId!!
|
||||
|
||||
val sharedIndex = aliceSession.cryptoService().getSharedWithInfo(e2eRoomID, megolmSessionId)
|
||||
.getObject(bobSession.myUserId, bobSession.sessionParams.deviceId)
|
||||
|
||||
assertEquals(0, sharedIndex, "The session received by bob should match what alice sent")
|
||||
assertEquals("The session received by bob should match what alice sent", 0, sharedIndex)
|
||||
|
||||
// Just send a real message as test
|
||||
val sentEvent = mTestHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.session.room.timeline
|
||||
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -29,7 +30,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
|||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.test.fail
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
|
@ -80,6 +80,7 @@ class TimelineWithManyMembersTest : InstrumentedTest {
|
|||
return@createEventListener true
|
||||
} else {
|
||||
fail("User " + session.myUserId + " decrypted as " + body + " CryptoError: " + it.root.mCryptoError)
|
||||
false
|
||||
}
|
||||
} ?: return@createEventListener false
|
||||
}
|
||||
|
|
|
@ -16,15 +16,19 @@
|
|||
|
||||
package org.matrix.android.sdk.session.space
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
|
@ -42,8 +46,6 @@ import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
|||
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
|
@ -56,22 +58,29 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
|
||||
val roomName = "My Space"
|
||||
val topic = "A public space for test"
|
||||
val spaceId: String
|
||||
runBlocking {
|
||||
spaceId = session.spaceService().createSpace(roomName, topic, null, true)
|
||||
// wait a bit to let the summary update it self :/
|
||||
delay(400)
|
||||
var spaceId: String = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
spaceId = session.spaceService().createSpace(roomName, topic, null, true)
|
||||
// wait a bit to let the summary update it self :/
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
assertEquals(roomName, syncedSpace?.asRoom()?.roomSummary()?.name, "Room name should be set")
|
||||
assertEquals(topic, syncedSpace?.asRoom()?.roomSummary()?.topic, "Room topic should be set")
|
||||
commonTestHelper.waitWithLatch {
|
||||
commonTestHelper.retryPeriodicallyWithLatch(it) {
|
||||
syncedSpace?.asRoom()?.roomSummary()?.name != null
|
||||
}
|
||||
}
|
||||
assertEquals("Room name should be set", roomName, syncedSpace?.asRoom()?.roomSummary()?.name)
|
||||
assertEquals("Room topic should be set", topic, syncedSpace?.asRoom()?.roomSummary()?.topic)
|
||||
// assertEquals(topic, syncedSpace.asRoom().roomSummary()?., "Room topic should be set")
|
||||
|
||||
assertNotNull(syncedSpace, "Space should be found by Id")
|
||||
val creationEvent = syncedSpace.asRoom().getStateEvent(EventType.STATE_ROOM_CREATE)
|
||||
assertNotNull("Space should be found by Id", syncedSpace)
|
||||
val creationEvent = syncedSpace!!.asRoom().getStateEvent(EventType.STATE_ROOM_CREATE)
|
||||
val createContent = creationEvent?.content.toModel<RoomCreateContent>()
|
||||
assertEquals(RoomType.SPACE, createContent?.type, "Room type should be space")
|
||||
assertEquals("Room type should be space", RoomType.SPACE, createContent?.type)
|
||||
|
||||
var powerLevelsContent: PowerLevelsContent? = null
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
|
@ -81,17 +90,17 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
toModel != null
|
||||
}
|
||||
}
|
||||
assertEquals(100, powerLevelsContent?.eventsDefault, "Space-rooms should be created with a power level for events_default of 100")
|
||||
assertEquals("Space-rooms should be created with a power level for events_default of 100", 100, powerLevelsContent?.eventsDefault)
|
||||
|
||||
val guestAccess = syncedSpace.asRoom().getStateEvent(EventType.STATE_ROOM_GUEST_ACCESS)?.content
|
||||
?.toModel<RoomGuestAccessContent>()?.guestAccess
|
||||
|
||||
assertEquals(GuestAccess.CanJoin, guestAccess, "Public space room should be peekable by guest")
|
||||
assertEquals("Public space room should be peekable by guest", GuestAccess.CanJoin, guestAccess)
|
||||
|
||||
val historyVisibility = syncedSpace.asRoom().getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY)?.content
|
||||
?.toModel<RoomHistoryVisibilityContent>()?.historyVisibility
|
||||
|
||||
assertEquals(RoomHistoryVisibility.WORLD_READABLE, historyVisibility, "Public space room should be world readable")
|
||||
assertEquals("Public space room should be world readable", RoomHistoryVisibility.WORLD_READABLE, historyVisibility)
|
||||
|
||||
commonTestHelper.signOutAndClose(session)
|
||||
}
|
||||
|
@ -120,8 +129,8 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
assertEquals(JoinSpaceResult.Success, joinResult)
|
||||
|
||||
val spaceBobPov = bobSession.spaceService().getSpace(spaceId)
|
||||
assertEquals(roomName, spaceBobPov?.asRoom()?.roomSummary()?.name, "Room name should be set")
|
||||
assertEquals(topic, spaceBobPov?.asRoom()?.roomSummary()?.topic, "Room topic should be set")
|
||||
assertEquals("Room name should be set", roomName, spaceBobPov?.asRoom()?.roomSummary()?.name)
|
||||
assertEquals("Room topic should be set", topic, spaceBobPov?.asRoom()?.roomSummary()?.topic)
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(bobSession)
|
||||
|
@ -139,54 +148,73 @@ class SpaceCreationTest : InstrumentedTest {
|
|||
val syncedSpace = aliceSession.spaceService().getSpace(spaceId)
|
||||
|
||||
// create a room
|
||||
val firstChild: String = runBlocking {
|
||||
aliceSession.createRoom(CreateRoomParams().apply {
|
||||
this.name = "FirstRoom"
|
||||
this.topic = "Description of first room"
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
})
|
||||
var firstChild: String? = null
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
firstChild = aliceSession.createRoom(CreateRoomParams().apply {
|
||||
this.name = "FirstRoom"
|
||||
this.topic = "Description of first room"
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
})
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
syncedSpace?.addChildren(firstChild, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", true)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", true, suggested = true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val secondChild: String = runBlocking {
|
||||
aliceSession.createRoom(CreateRoomParams().apply {
|
||||
this.name = "SecondRoom"
|
||||
this.topic = "Description of second room"
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
})
|
||||
var secondChild: String? = null
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
secondChild = aliceSession.createRoom(CreateRoomParams().apply {
|
||||
this.name = "SecondRoom"
|
||||
this.topic = "Description of second room"
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
})
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
syncedSpace?.addChildren(secondChild, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", false)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", false, suggested = true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
// Try to join from bob, it's a public space no need to invite
|
||||
|
||||
val joinResult = runBlocking {
|
||||
bobSession.spaceService().joinSpace(spaceId)
|
||||
var joinResult: JoinSpaceResult? = null
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
joinResult = bobSession.spaceService().joinSpace(spaceId)
|
||||
// wait a bit to let the summary update it self :/
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(JoinSpaceResult.Success, joinResult)
|
||||
|
||||
val spaceBobPov = bobSession.spaceService().getSpace(spaceId)
|
||||
assertEquals(roomName, spaceBobPov?.asRoom()?.roomSummary()?.name, "Room name should be set")
|
||||
assertEquals(topic, spaceBobPov?.asRoom()?.roomSummary()?.topic, "Room topic should be set")
|
||||
assertEquals("Room name should be set", roomName, spaceBobPov?.asRoom()?.roomSummary()?.name)
|
||||
assertEquals("Room topic should be set", topic, spaceBobPov?.asRoom()?.roomSummary()?.topic)
|
||||
|
||||
// check if bob has joined automatically the first room
|
||||
|
||||
val bobMembershipFirstRoom = bobSession.getRoom(firstChild)?.roomSummary()?.membership
|
||||
assertEquals(Membership.JOIN, bobMembershipFirstRoom, "Bob should have joined this room")
|
||||
val bobMembershipFirstRoom = bobSession.getRoomSummary(firstChild!!)?.membership
|
||||
assertEquals("Bob should have joined this room", Membership.JOIN, bobMembershipFirstRoom)
|
||||
RoomSummaryQueryParams.Builder()
|
||||
|
||||
val spaceSummaryBobPov = bobSession.spaceService().getSpaceSummaries(roomSummaryQueryParams {
|
||||
this.roomId = QueryStringValue.Equals(spaceId)
|
||||
this.memberships = listOf(Membership.JOIN)
|
||||
}).firstOrNull()
|
||||
val childCount = bobSession.getRoomSummaries(
|
||||
roomSummaryQueryParams {
|
||||
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(spaceId)
|
||||
}
|
||||
).size
|
||||
|
||||
assertEquals(2, spaceSummaryBobPov?.spaceChildren?.size ?: -1, "Unexpected number of children")
|
||||
assertEquals("Unexpected number of joined children", 1, childCount)
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(bobSession)
|
||||
|
|
|
@ -20,15 +20,18 @@ import android.util.Log
|
|||
import androidx.lifecycle.Observer
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
|
@ -36,9 +39,6 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
|||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
|
@ -51,27 +51,38 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
val spaceName = "My Space"
|
||||
val topic = "A public space for test"
|
||||
val spaceId: String
|
||||
runBlocking {
|
||||
spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
|
||||
// wait a bit to let the summary update it self :/
|
||||
delay(400)
|
||||
var spaceId: String = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
|
||||
val roomId = runBlocking {
|
||||
session.createRoom(CreateRoomParams().apply { name = "General" })
|
||||
var roomId: String = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
roomId = session.createRoom(CreateRoomParams().apply { name = "General" })
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
|
||||
runBlocking {
|
||||
syncedSpace!!.addChildren(roomId, viaServers, null, true)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
syncedSpace!!.addChildren(roomId, viaServers, null, true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(9000)
|
||||
|
@ -84,60 +95,80 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
assertNotNull(parents)
|
||||
assertEquals(1, parents.size)
|
||||
assertEquals(1, parents!!.size)
|
||||
assertEquals(spaceName, parents.first().roomSummary?.name)
|
||||
|
||||
assertNotNull(canonicalParents)
|
||||
assertEquals(1, canonicalParents.size)
|
||||
assertEquals(1, canonicalParents!!.size)
|
||||
assertEquals(spaceName, canonicalParents.first().roomSummary?.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateChildRelations() {
|
||||
val session = commonTestHelper.createAccount("Jhon", SessionTestParams(true))
|
||||
val spaceName = "My Space"
|
||||
val topic = "A public space for test"
|
||||
Log.d("## TEST", "Before")
|
||||
val spaceId = runBlocking {
|
||||
session.spaceService().createSpace(spaceName, topic, null, true)
|
||||
}
|
||||
|
||||
Log.d("## TEST", "created space $spaceId ${Thread.currentThread()}")
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
|
||||
val children = listOf("General" to true /*canonical*/, "Random" to false)
|
||||
|
||||
val roomIdList = children.map {
|
||||
runBlocking {
|
||||
session.createRoom(CreateRoomParams().apply { name = it.first })
|
||||
} to it.second
|
||||
}
|
||||
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
|
||||
runBlocking {
|
||||
roomIdList.forEach { entry ->
|
||||
syncedSpace!!.addChildren(entry.first, viaServers, null, true)
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
roomIdList.forEach {
|
||||
session.spaceService().setSpaceParent(it.first, spaceId, it.second, viaServers)
|
||||
}
|
||||
delay(400)
|
||||
}
|
||||
|
||||
roomIdList.forEach {
|
||||
val parents = session.getRoom(it.first)?.roomSummary()?.spaceParents
|
||||
val canonicalParents = session.getRoom(it.first)?.roomSummary()?.spaceParents?.filter { it.canonical == true }
|
||||
|
||||
assertNotNull(parents)
|
||||
assertEquals(1, parents.size, "Unexpected number of parent")
|
||||
assertEquals(spaceName, parents.first().roomSummary?.name, "Unexpected parent name ")
|
||||
assertEquals(if (it.second) 1 else 0, canonicalParents?.size ?: 0, "Parent of ${it.first} should be canonical ${it.second}")
|
||||
}
|
||||
}
|
||||
// @Test
|
||||
// fun testCreateChildRelations() {
|
||||
// val session = commonTestHelper.createAccount("Jhon", SessionTestParams(true))
|
||||
// val spaceName = "My Space"
|
||||
// val topic = "A public space for test"
|
||||
// Log.d("## TEST", "Before")
|
||||
//
|
||||
// var spaceId = ""
|
||||
// commonTestHelper.waitWithLatch {
|
||||
// GlobalScope.launch {
|
||||
// spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
|
||||
// it.countDown()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Log.d("## TEST", "created space $spaceId ${Thread.currentThread()}")
|
||||
// val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
//
|
||||
// val children = listOf("General" to true /*canonical*/, "Random" to false)
|
||||
//
|
||||
// // val roomIdList = children.map {
|
||||
// // runBlocking {
|
||||
// // session.createRoom(CreateRoomParams().apply { name = it.first })
|
||||
// // } to it.second
|
||||
// // }
|
||||
// val roomIdList = mutableListOf<Pair<String, Boolean>>()
|
||||
// commonTestHelper.waitWithLatch {
|
||||
// GlobalScope.launch {
|
||||
// children.forEach {
|
||||
// val rID = session.createRoom(CreateRoomParams().apply { name = it.first })
|
||||
// roomIdList.add(rID to it.second)
|
||||
// }
|
||||
// it.countDown()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
//
|
||||
// commonTestHelper.waitWithLatch {
|
||||
// GlobalScope.launch {
|
||||
// roomIdList.forEach { entry ->
|
||||
// syncedSpace!!.addChildren(entry.first, viaServers, null, true)
|
||||
// }
|
||||
// it.countDown()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// commonTestHelper.waitWithLatch {
|
||||
// GlobalScope.launch {
|
||||
// roomIdList.forEach {
|
||||
// session.spaceService().setSpaceParent(it.first, spaceId, it.second, viaServers)
|
||||
// }
|
||||
// it.countDown()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// roomIdList.forEach {
|
||||
// val parents = session.getRoom(it.first)?.roomSummary()?.spaceParents
|
||||
// val canonicalParents = session.getRoom(it.first)?.roomSummary()?.spaceParents?.filter { it.canonical == true }
|
||||
//
|
||||
// assertNotNull(parents)
|
||||
// assertEquals("Unexpected number of parent", 1, parents!!.size)
|
||||
// assertEquals("Unexpected parent name", spaceName, parents.first().roomSummary?.name)
|
||||
// assertEquals("Parent of ${it.first} should be canonical ${it.second}", if (it.second) 1 else 0, canonicalParents?.size ?: 0)
|
||||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
fun testFilteringBySpace() {
|
||||
|
@ -162,50 +193,64 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
// add C as a subspace of A
|
||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
// Create orphan rooms
|
||||
|
||||
val orphan1 = runBlocking {
|
||||
session.createRoom(CreateRoomParams().apply { name = "O1" })
|
||||
var orphan1 = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" })
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
val orphan2 = runBlocking {
|
||||
session.createRoom(CreateRoomParams().apply { name = "O2" })
|
||||
|
||||
var orphan2 = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" })
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) })
|
||||
|
||||
assertEquals(9, allRooms.size, "Unexpected number of rooms")
|
||||
assertEquals("Unexpected number of rooms", 9, allRooms.size)
|
||||
|
||||
val orphans = session.getFlattenRoomSummaryChildrenOf(null)
|
||||
|
||||
assertEquals(2, orphans.size, "Unexpected number of orphan rooms")
|
||||
assertTrue(orphans.indexOfFirst { it.roomId == orphan1 } != -1, "O1 should be an orphan")
|
||||
assertTrue(orphans.indexOfFirst { it.roomId == orphan2 } != -1, "O2 should be an orphan ${orphans.map { it.name }}")
|
||||
assertEquals("Unexpected number of orphan rooms", 2, orphans.size)
|
||||
assertTrue("O1 should be an orphan", orphans.any { it.roomId == orphan1 })
|
||||
assertTrue("O2 should be an orphan ${orphans.map { it.name }}", orphans.any { it.roomId == orphan2 })
|
||||
|
||||
val aChildren = session.getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId)
|
||||
|
||||
assertEquals(4, aChildren.size, "Unexpected number of flatten child rooms")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A1" } != -1, "A1 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A2" } != -1, "A2 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C1" } != -1, "CA should be a grand child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C2" } != -1, "A1 should be a grand child of A")
|
||||
assertEquals("Unexpected number of flatten child rooms", 4, aChildren.size)
|
||||
assertTrue("A1 should be a child of A", aChildren.any { it.name == "A1" })
|
||||
assertTrue("A2 should be a child of A", aChildren.any { it.name == "A2" })
|
||||
assertTrue("CA should be a grand child of A", aChildren.any { it.name == "C1" })
|
||||
assertTrue("A1 should be a grand child of A", aChildren.any { it.name == "C2" })
|
||||
|
||||
// Add a non canonical child and check that it does not appear as orphan
|
||||
val a3 = runBlocking {
|
||||
session.createRoom(CreateRoomParams().apply { name = "A3" })
|
||||
}
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(a3, viaServers, null, false)
|
||||
delay(400)
|
||||
// here we do not set the parent!!
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" })
|
||||
spaceA!!.addChildren(a3, viaServers, null, false)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val orphansUpdate = session.getFlattenRoomSummaryChildrenOf(null)
|
||||
assertEquals(2, orphansUpdate.size, "Unexpected number of orphan rooms ${orphansUpdate.map { it.name }}")
|
||||
Thread.sleep(2_000)
|
||||
val orphansUpdate = session.getRoomSummaries(roomSummaryQueryParams {
|
||||
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null)
|
||||
})
|
||||
assertEquals("Unexpected number of orphan rooms ${orphansUpdate.map { it.name }}", 2, orphansUpdate.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -225,15 +270,21 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
// add C as a subspace of A
|
||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
// add back A as subspace of C
|
||||
runBlocking {
|
||||
val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
|
||||
spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
|
||||
spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
@ -242,11 +293,11 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
|
||||
val aChildren = session.getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId)
|
||||
|
||||
assertEquals(4, aChildren.size, "Unexpected number of flatten child rooms ${aChildren.map { it.name }}")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A1" } != -1, "A1 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A2" } != -1, "A2 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C1" } != -1, "CA should be a grand child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C2" } != -1, "A1 should be a grand child of A")
|
||||
assertEquals("Unexpected number of flatten child rooms ${aChildren.map { it.name }}", 4, aChildren.size)
|
||||
assertTrue("A1 should be a child of A", aChildren.any { it.name == "A1" })
|
||||
assertTrue("A2 should be a child of A", aChildren.any { it.name == "A2" })
|
||||
assertTrue("CA should be a grand child of A", aChildren.any { it.name == "C1" })
|
||||
assertTrue("A1 should be a grand child of A", aChildren.any { it.name == "C2" })
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -282,9 +333,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
override fun onChanged(children: List<RoomSummary>?) {
|
||||
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}")
|
||||
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
|
||||
if (children?.indexOfFirst { it.name == "C1" } != -1
|
||||
&& children?.indexOfFirst { it.name == "C2" } != -1
|
||||
) {
|
||||
if (children?.any { it.name == "C1" } == true && children.any { it.name == "C2" }) {
|
||||
// B1 has been added live!
|
||||
latch.countDown()
|
||||
flatAChildren.removeObserver(this)
|
||||
|
@ -345,8 +394,12 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||
/** Name, auto-join, canonical*/
|
||||
): TestSpaceCreationResult {
|
||||
val spaceId = runBlocking {
|
||||
session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
||||
var spaceId = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
|
@ -354,9 +407,14 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
|
||||
val roomIds =
|
||||
childInfo.map { entry ->
|
||||
runBlocking {
|
||||
session.createRoom(CreateRoomParams().apply { name = entry.first })
|
||||
var roomId = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
roomId = session.createRoom(CreateRoomParams().apply { name = entry.first })
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
roomId
|
||||
}
|
||||
|
||||
roomIds.forEachIndexed { index, roomId ->
|
||||
|
@ -403,12 +461,12 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
// + A
|
||||
// a1, a2
|
||||
// + B
|
||||
// b1, b2, b3
|
||||
// b1, b2, b3
|
||||
// + C
|
||||
// + c1, c2
|
||||
|
||||
val rootSpaces = session.spaceService().getRootSpaceSummaries()
|
||||
|
||||
assertEquals(2, rootSpaces.size, "Unexpected number of root spaces ${rootSpaces.map { it.name }}")
|
||||
assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ data class MatrixError(
|
|||
// For M_LIMIT_EXCEEDED
|
||||
@Json(name = "retry_after_ms") val retryAfterMillis: Long? = null,
|
||||
// For M_UNKNOWN_TOKEN
|
||||
@Json(name = "soft_logout") val isSoftLogout: Boolean = false,
|
||||
@Json(name = "soft_logout") val isSoftLogout: Boolean? = null,
|
||||
// For M_INVALID_PEPPER
|
||||
// {"error": "pepper does not match 'erZvr'", "lookup_pepper": "pQgMS", "algorithm": "sha256", "errcode": "M_INVALID_PEPPER"}
|
||||
@Json(name = "lookup_pepper") val newLookupPepper: String? = null,
|
||||
|
|
|
@ -31,6 +31,8 @@ interface ContentUploadStateTracker {
|
|||
sealed class State {
|
||||
object Idle : State()
|
||||
object EncryptingThumbnail : State()
|
||||
object CompressingImage : State()
|
||||
data class CompressingVideo(val percent: Float) : State()
|
||||
data class UploadingThumbnail(val current: Long, val total: Long) : State()
|
||||
data class Encrypting(val current: Long, val total: Long) : State()
|
||||
data class Uploading(val current: Long, val total: Long) : State()
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
|||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.json.JSONObject
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import timber.log.Timber
|
||||
|
||||
typealias Content = JsonDict
|
||||
|
@ -90,6 +92,16 @@ data class Event(
|
|||
@Transient
|
||||
var sendState: SendState = SendState.UNKNOWN
|
||||
|
||||
@Transient
|
||||
var sendStateDetails: String? = null
|
||||
|
||||
fun sendStateError(): MatrixError? {
|
||||
return sendStateDetails?.let {
|
||||
val matrixErrorAdapter = MoshiProvider.providesMoshi().adapter(MatrixError::class.java)
|
||||
tryOrNull { matrixErrorAdapter.fromJson(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `age` value transcoded in a timestamp based on the device clock when the SDK received
|
||||
* the event from the home server.
|
||||
|
|
|
@ -53,11 +53,9 @@ object EventType {
|
|||
const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access"
|
||||
const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels"
|
||||
|
||||
// const val STATE_SPACE_CHILD = "m.space.child"
|
||||
const val STATE_SPACE_CHILD = "org.matrix.msc1772.space.child"
|
||||
const val STATE_SPACE_CHILD = "m.space.child"
|
||||
|
||||
// const val STATE_SPACE_PARENT = "m.space.parent"
|
||||
const val STATE_SPACE_PARENT = "org.matrix.msc1772.space.parent"
|
||||
const val STATE_SPACE_PARENT = "m.space.parent"
|
||||
|
||||
/**
|
||||
* Note that this Event has been deprecated, see
|
||||
|
|
|
@ -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
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -58,7 +58,7 @@ data class RoomSummaryQueryParams(
|
|||
val roomTagQueryFilter: RoomTagQueryFilter?,
|
||||
val excludeType: List<String?>?,
|
||||
val includeType: List<String?>?,
|
||||
val activeSpaceId: ActiveSpaceFilter?,
|
||||
val activeSpaceFilter: ActiveSpaceFilter?,
|
||||
var activeGroupId: String? = null
|
||||
) {
|
||||
|
||||
|
@ -72,7 +72,7 @@ data class RoomSummaryQueryParams(
|
|||
var roomTagQueryFilter: RoomTagQueryFilter? = null
|
||||
var excludeType: List<String?>? = listOf(RoomType.SPACE)
|
||||
var includeType: List<String?>? = null
|
||||
var activeSpaceId: ActiveSpaceFilter = ActiveSpaceFilter.None
|
||||
var activeSpaceFilter: ActiveSpaceFilter = ActiveSpaceFilter.None
|
||||
var activeGroupId: String? = null
|
||||
|
||||
fun build() = RoomSummaryQueryParams(
|
||||
|
@ -84,7 +84,7 @@ data class RoomSummaryQueryParams(
|
|||
roomTagQueryFilter = roomTagQueryFilter,
|
||||
excludeType = excludeType,
|
||||
includeType = includeType,
|
||||
activeSpaceId = activeSpaceId,
|
||||
activeSpaceFilter = activeSpaceFilter,
|
||||
activeGroupId = activeGroupId
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,31 +16,35 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.room.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#room-history-visibility
|
||||
*/
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class RoomHistoryVisibility {
|
||||
/**
|
||||
* All events while this is the m.room.history_visibility value may be shared by any
|
||||
* participating homeserver with anyone, regardless of whether they have ever joined the room.
|
||||
*/
|
||||
WORLD_READABLE,
|
||||
@Json(name = "world_readable") WORLD_READABLE,
|
||||
|
||||
/**
|
||||
* Previous events are always accessible to newly joined members. All events in the
|
||||
* room are accessible, even those sent when the member was not a part of the room.
|
||||
*/
|
||||
SHARED,
|
||||
@Json(name = "shared") SHARED,
|
||||
|
||||
/**
|
||||
* Events are accessible to newly joined members from the point they were invited onwards.
|
||||
* Events stop being accessible when the member's state changes to something other than invite or join.
|
||||
*/
|
||||
INVITED,
|
||||
@Json(name = "invited") INVITED,
|
||||
|
||||
/**
|
||||
* Events are accessible to newly joined members from the point they joined the room onwards.
|
||||
* Events stop being accessible when the member's state changes to something other than join.
|
||||
*/
|
||||
JOINED
|
||||
@Json(name = "joined") JOINED
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
|||
* 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 = "",
|
||||
|
@ -36,6 +36,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,
|
||||
|
@ -81,6 +82,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
|
||||
|
|
|
@ -18,6 +18,5 @@ package org.matrix.android.sdk.api.session.room.model
|
|||
|
||||
object RoomType {
|
||||
|
||||
const val SPACE = "org.matrix.msc1772.space" // "m.space"
|
||||
// const val MESSAGING = "org.matrix.msc1840.messaging"
|
||||
const val SPACE = "m.space"
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
)
|
||||
|
|
|
@ -160,6 +160,6 @@ open class CreateRoomParams {
|
|||
|
||||
companion object {
|
||||
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
|
||||
private const val CREATION_CONTENT_KEY_ROOM_TYPE = "org.matrix.msc1772.type"
|
||||
private const val CREATION_CONTENT_KEY_ROOM_TYPE = "type"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,5 +28,5 @@ data class RoomCreateContent(
|
|||
@Json(name = "room_version") val roomVersion: String? = null,
|
||||
@Json(name = "predecessor") val predecessor: Predecessor? = null,
|
||||
// Defines the room type, see #RoomType (user extensible)
|
||||
@Json(name = "org.matrix.msc1772.type") val type: String? = null
|
||||
@Json(name = "type") val type: String? = null
|
||||
)
|
||||
|
|
|
@ -47,3 +47,10 @@ data class FileInfo(
|
|||
*/
|
||||
@Json(name = "thumbnail_file") val thumbnailFile: EncryptedFileInfo? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the url of the encrypted thumbnail or of the thumbnail
|
||||
*/
|
||||
fun FileInfo.getThumbnailUrl(): String? {
|
||||
return thumbnailFile?.url ?: thumbnailUrl
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ data class ImageInfo(
|
|||
/**
|
||||
* Size of the image in bytes.
|
||||
*/
|
||||
@Json(name = "size") val size: Int = 0,
|
||||
@Json(name = "size") val size: Long = 0,
|
||||
|
||||
/**
|
||||
* Metadata about the image referred to in thumbnail_url.
|
||||
|
@ -57,3 +57,10 @@ data class ImageInfo(
|
|||
*/
|
||||
@Json(name = "thumbnail_file") val thumbnailFile: EncryptedFileInfo? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the url of the encrypted thumbnail or of the thumbnail
|
||||
*/
|
||||
fun ImageInfo.getThumbnailUrl(): String? {
|
||||
return thumbnailFile?.url ?: thumbnailUrl
|
||||
}
|
||||
|
|
|
@ -37,3 +37,10 @@ data class LocationInfo(
|
|||
*/
|
||||
@Json(name = "thumbnail_file") val thumbnailFile: EncryptedFileInfo? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the url of the encrypted thumbnail or of the thumbnail
|
||||
*/
|
||||
fun LocationInfo.getThumbnailUrl(): String? {
|
||||
return thumbnailFile?.url ?: thumbnailUrl
|
||||
}
|
||||
|
|
|
@ -62,3 +62,10 @@ data class VideoInfo(
|
|||
*/
|
||||
@Json(name = "thumbnail_file") val thumbnailFile: EncryptedFileInfo? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the url of the encrypted thumbnail or of the thumbnail
|
||||
*/
|
||||
fun VideoInfo.getThumbnailUrl(): String? {
|
||||
return thumbnailFile?.url ?: thumbnailUrl
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
|
|||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.toMatrixErrorStr
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SendVerificationMessageTask : Task<SendVerificationMessageTask.Params, String> {
|
||||
|
@ -55,7 +56,7 @@ internal class DefaultSendVerificationMessageTask @Inject constructor(
|
|||
localEchoRepository.updateSendState(localId, event.roomId, SendState.SENT)
|
||||
return response.eventId
|
||||
} catch (e: Throwable) {
|
||||
localEchoRepository.updateSendState(localId, event.roomId, SendState.UNDELIVERED)
|
||||
localEchoRepository.updateSendState(localId, event.roomId, SendState.UNDELIVERED, e.toMatrixErrorStr())
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -48,7 +49,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
const val SESSION_STORE_SCHEMA_SC_VERSION = 2L
|
||||
const val SESSION_STORE_SCHEMA_SC_VERSION_OFFSET = (1L shl 12)
|
||||
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 10L +
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 12L +
|
||||
SESSION_STORE_SCHEMA_SC_VERSION * SESSION_STORE_SCHEMA_SC_VERSION_OFFSET
|
||||
}
|
||||
|
||||
|
@ -68,6 +69,8 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
if (oldVersion <= 7) migrateTo8(realm)
|
||||
if (oldVersion <= 8) migrateTo9(realm)
|
||||
if (oldVersion <= 9) migrateTo10(realm)
|
||||
if (oldVersion <= 10) migrateTo11(realm)
|
||||
if (oldVersion <= 11) migrateTo12(realm)
|
||||
|
||||
if (oldScVersion <= 0) migrateToSc1(realm)
|
||||
if (oldScVersion <= 1) migrateToSc2(realm)
|
||||
|
@ -171,10 +174,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)
|
||||
|
@ -189,9 +188,13 @@ 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
|
||||
}
|
||||
|
||||
fun migrateTo9(realm: DynamicRealm) {
|
||||
private fun migrateTo9(realm: DynamicRealm) {
|
||||
Timber.d("Step 8 -> 9")
|
||||
|
||||
realm.schema.get("RoomSummaryEntity")
|
||||
|
@ -229,7 +232,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
}
|
||||
}
|
||||
|
||||
fun migrateTo10(realm: DynamicRealm) {
|
||||
private fun migrateTo10(realm: DynamicRealm) {
|
||||
Timber.d("Step 9 -> 10")
|
||||
realm.schema.create("SpaceChildSummaryEntity")
|
||||
?.addField(SpaceChildSummaryEntityFields.ORDER, String::class.java)
|
||||
|
@ -268,4 +271,35 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
?.addRealmListField(RoomSummaryEntityFields.PARENTS.`$`, realm.schema.get("SpaceParentSummaryEntity")!!)
|
||||
?.addRealmListField(RoomSummaryEntityFields.CHILDREN.`$`, realm.schema.get("SpaceChildSummaryEntity")!!)
|
||||
}
|
||||
|
||||
private fun migrateTo11(realm: DynamicRealm) {
|
||||
Timber.d("Step 10 -> 11")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ internal object EventMapper {
|
|||
).also {
|
||||
it.ageLocalTs = eventEntity.ageLocalTs
|
||||
it.sendState = eventEntity.sendState
|
||||
it.sendStateDetails = eventEntity.sendStateDetails
|
||||
eventEntity.decryptionResultJson?.let { json ->
|
||||
try {
|
||||
it.mxDecryptionResult = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java).fromJson(json)
|
||||
|
|
|
@ -50,6 +50,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,
|
||||
|
@ -99,7 +100,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()
|
||||
|
|
|
@ -32,6 +32,8 @@ internal open class EventEntity(@Index var eventId: String = "",
|
|||
@Index var stateKey: String? = null,
|
||||
var originServerTs: Long? = null,
|
||||
@Index var sender: String? = null,
|
||||
// Can contain a serialized MatrixError
|
||||
var sendStateDetails: String? = null,
|
||||
var age: Long? = 0,
|
||||
var unsignedData: String? = null,
|
||||
var redacts: String? = null,
|
||||
|
|
|
@ -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
|
||||
|
@ -278,6 +280,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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.failure.MatrixError
|
|||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.ResponseBody
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
import timber.log.Timber
|
||||
|
@ -91,7 +92,7 @@ private fun toFailure(errorBody: ResponseBody?, httpCode: Int, globalErrorReceiv
|
|||
} else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */
|
||||
&& matrixError.code == MatrixError.M_UNKNOWN_TOKEN) {
|
||||
// Also send this error to the globalErrorReceiver, for a global management
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout))
|
||||
globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout.orFalse()))
|
||||
}
|
||||
|
||||
return Failure.ServerError(matrixError, httpCode)
|
||||
|
|
|
@ -78,6 +78,16 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU
|
|||
updateState(key, progressData)
|
||||
}
|
||||
|
||||
internal fun setCompressingImage(key: String) {
|
||||
val progressData = ContentUploadStateTracker.State.CompressingImage
|
||||
updateState(key, progressData)
|
||||
}
|
||||
|
||||
internal fun setCompressingVideo(key: String, percent: Float) {
|
||||
val progressData = ContentUploadStateTracker.State.CompressingVideo(percent)
|
||||
updateState(key, progressData)
|
||||
}
|
||||
|
||||
internal fun setProgress(key: String, current: Long, total: Long) {
|
||||
val progressData = ContentUploadStateTracker.State.Uploading(current, total)
|
||||
updateState(key, progressData)
|
||||
|
|
|
@ -31,22 +31,28 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||
import okio.BufferedSink
|
||||
import okio.source
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.internal.di.Authenticated
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.ProgressRequestBody
|
||||
import org.matrix.android.sdk.internal.network.awaitResponse
|
||||
import org.matrix.android.sdk.internal.network.toFailure
|
||||
import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.internal.util.TemporaryFileCreator
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class FileUploader @Inject constructor(@Authenticated
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService,
|
||||
private val context: Context,
|
||||
private val temporaryFileCreator: TemporaryFileCreator,
|
||||
contentUrlResolver: ContentUrlResolver,
|
||||
moshi: Moshi) {
|
||||
|
||||
|
@ -57,6 +63,21 @@ internal class FileUploader @Inject constructor(@Authenticated
|
|||
filename: String?,
|
||||
mimeType: String?,
|
||||
progressListener: ProgressRequestBody.Listener? = null): ContentUploadResponse {
|
||||
// Check size limit
|
||||
val maxUploadFileSize = homeServerCapabilitiesService.getHomeServerCapabilities().maxUploadFileSize
|
||||
|
||||
if (maxUploadFileSize != HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN
|
||||
&& file.length() > maxUploadFileSize) {
|
||||
// Known limitation and file too big for the server, save the pain to upload it
|
||||
throw Failure.ServerError(
|
||||
error = MatrixError(
|
||||
code = MatrixError.M_TOO_LARGE,
|
||||
message = "Cannot upload files larger than ${maxUploadFileSize / 1048576L}mb"
|
||||
),
|
||||
httpCode = 413
|
||||
)
|
||||
}
|
||||
|
||||
val uploadBody = object : RequestBody() {
|
||||
override fun contentLength() = file.length()
|
||||
|
||||
|
@ -90,7 +111,7 @@ internal class FileUploader @Inject constructor(@Authenticated
|
|||
val inputStream = withContext(Dispatchers.IO) {
|
||||
context.contentResolver.openInputStream(uri)
|
||||
} ?: throw FileNotFoundException()
|
||||
val workingFile = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
|
||||
val workingFile = temporaryFileCreator.create()
|
||||
workingFile.outputStream().use {
|
||||
inputStream.copyTo(it)
|
||||
}
|
||||
|
|
|
@ -16,19 +16,20 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.content
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.internal.util.TemporaryFileCreator
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class ImageCompressor @Inject constructor(private val context: Context) {
|
||||
internal class ImageCompressor @Inject constructor(
|
||||
private val temporaryFileCreator: TemporaryFileCreator
|
||||
) {
|
||||
suspend fun compress(
|
||||
imageFile: File,
|
||||
desiredWidth: Int,
|
||||
|
@ -45,7 +46,7 @@ internal class ImageCompressor @Inject constructor(private val context: Context)
|
|||
}
|
||||
} ?: return@withContext imageFile
|
||||
|
||||
val destinationFile = createDestinationFile()
|
||||
val destinationFile = temporaryFileCreator.create()
|
||||
|
||||
runCatching {
|
||||
destinationFile.outputStream().use {
|
||||
|
@ -53,7 +54,7 @@ internal class ImageCompressor @Inject constructor(private val context: Context)
|
|||
}
|
||||
}
|
||||
|
||||
return@withContext destinationFile
|
||||
destinationFile
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,16 +65,16 @@ internal class ImageCompressor @Inject constructor(private val context: Context)
|
|||
val orientation = exifInfo.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
val matrix = Matrix()
|
||||
when (orientation) {
|
||||
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
|
||||
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
|
||||
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
|
||||
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
|
||||
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
|
||||
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
|
||||
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.preScale(-1f, 1f)
|
||||
ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.preScale(1f, -1f)
|
||||
ExifInterface.ORIENTATION_TRANSPOSE -> {
|
||||
ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.preScale(1f, -1f)
|
||||
ExifInterface.ORIENTATION_TRANSPOSE -> {
|
||||
matrix.preRotate(-90f)
|
||||
matrix.preScale(-1f, 1f)
|
||||
}
|
||||
ExifInterface.ORIENTATION_TRANSVERSE -> {
|
||||
ExifInterface.ORIENTATION_TRANSVERSE -> {
|
||||
matrix.preRotate(90f)
|
||||
matrix.preScale(-1f, 1f)
|
||||
}
|
||||
|
@ -116,8 +117,4 @@ internal class ImageCompressor @Inject constructor(private val context: Context)
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDestinationFile(): File {
|
||||
return File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,12 @@ package org.matrix.android.sdk.internal.session.content
|
|||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.media.MediaMetadataRetriever
|
||||
import androidx.core.net.toUri
|
||||
import androidx.work.WorkerParameters
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
|
@ -41,12 +44,13 @@ import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker
|
|||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoIdentifiers
|
||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
||||
import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
||||
import org.matrix.android.sdk.internal.util.TemporaryFileCreator
|
||||
import org.matrix.android.sdk.internal.util.toMatrixErrorStr
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
private data class NewAttachmentAttributes(
|
||||
|
@ -77,7 +81,9 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
@Inject lateinit var fileService: DefaultFileService
|
||||
@Inject lateinit var cancelSendTracker: CancelSendTracker
|
||||
@Inject lateinit var imageCompressor: ImageCompressor
|
||||
@Inject lateinit var videoCompressor: VideoCompressor
|
||||
@Inject lateinit var localEchoRepository: LocalEchoRepository
|
||||
@Inject lateinit var temporaryFileCreator: TemporaryFileCreator
|
||||
|
||||
override fun injectWith(injector: SessionComponent) {
|
||||
injector.inject(this)
|
||||
|
@ -109,7 +115,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
val attachment = params.attachment
|
||||
val filesToDelete = mutableListOf<File>()
|
||||
|
||||
try {
|
||||
return try {
|
||||
val inputStream = context.contentResolver.openInputStream(attachment.queryUri)
|
||||
?: return Result.success(
|
||||
WorkerParamsFactory.toData(
|
||||
|
@ -120,7 +126,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
)
|
||||
|
||||
// always use a temporary file, it guaranties that we could report progress on upload and simplifies the flows
|
||||
val workingFile = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
|
||||
val workingFile = temporaryFileCreator.create()
|
||||
.also { filesToDelete.add(it) }
|
||||
workingFile.outputStream().use { outputStream ->
|
||||
inputStream.use { inputStream ->
|
||||
|
@ -128,8 +134,6 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
}
|
||||
}
|
||||
|
||||
val uploadThumbnailResult = dealWithThumbnail(params)
|
||||
|
||||
val progressListener = object : ProgressRequestBody.Listener {
|
||||
override fun onProgress(current: Long, total: Long) {
|
||||
notifyTracker(params) {
|
||||
|
@ -144,7 +148,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
|
||||
var uploadedFileEncryptedFileInfo: EncryptedFileInfo? = null
|
||||
|
||||
return try {
|
||||
try {
|
||||
val fileToUpload: File
|
||||
var newAttachmentAttributes = NewAttachmentAttributes(
|
||||
params.attachment.width?.toInt(),
|
||||
|
@ -156,6 +160,8 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
// Do not compress gif
|
||||
&& attachment.mimeType != MimeTypes.Gif
|
||||
&& params.compressBeforeSending) {
|
||||
notifyTracker(params) { contentUploadStateTracker.setCompressingImage(it) }
|
||||
|
||||
fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
|
||||
.also { compressedFile ->
|
||||
// Get new Bitmap size
|
||||
|
@ -170,6 +176,48 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
}
|
||||
}
|
||||
.also { filesToDelete.add(it) }
|
||||
} else if (attachment.type == ContentAttachmentData.Type.VIDEO
|
||||
// Do not compress gif
|
||||
&& attachment.mimeType != MimeTypes.Gif
|
||||
&& params.compressBeforeSending) {
|
||||
fileToUpload = videoCompressor.compress(workingFile, object : ProgressListener {
|
||||
override fun onProgress(progress: Int, total: Int) {
|
||||
notifyTracker(params) { contentUploadStateTracker.setCompressingVideo(it, progress.toFloat()) }
|
||||
}
|
||||
})
|
||||
.let { videoCompressionResult ->
|
||||
when (videoCompressionResult) {
|
||||
is VideoCompressionResult.Success -> {
|
||||
val compressedFile = videoCompressionResult.compressedFile
|
||||
var compressedWidth: Int? = null
|
||||
var compressedHeight: Int? = null
|
||||
|
||||
tryOrNull {
|
||||
context.contentResolver.openFileDescriptor(compressedFile.toUri(), "r")?.use { pfd ->
|
||||
MediaMetadataRetriever().let {
|
||||
it.setDataSource(pfd.fileDescriptor)
|
||||
compressedWidth = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt()
|
||||
compressedHeight = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get new Video file size and dimensions
|
||||
newAttachmentAttributes = newAttachmentAttributes.copy(
|
||||
newFileSize = compressedFile.length(),
|
||||
newWidth = compressedWidth ?: newAttachmentAttributes.newWidth,
|
||||
newHeight = compressedHeight ?: newAttachmentAttributes.newHeight
|
||||
)
|
||||
compressedFile
|
||||
.also { filesToDelete.add(it) }
|
||||
}
|
||||
VideoCompressionResult.CompressionNotNeeded,
|
||||
VideoCompressionResult.CompressionCancelled,
|
||||
is VideoCompressionResult.CompressionFailed -> {
|
||||
workingFile
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fileToUpload = workingFile
|
||||
// Fix: OpenableColumns.SIZE may return -1 or 0
|
||||
|
@ -180,9 +228,9 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
|
||||
val encryptedFile: File?
|
||||
val contentUploadResponse = if (params.isEncrypted) {
|
||||
Timber.v("## FileService: Encrypt file")
|
||||
Timber.v("## Encrypt file")
|
||||
|
||||
encryptedFile = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
|
||||
encryptedFile = temporaryFileCreator.create()
|
||||
.also { filesToDelete.add(it) }
|
||||
|
||||
uploadedFileEncryptedFileInfo =
|
||||
|
@ -192,18 +240,18 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
}
|
||||
}
|
||||
|
||||
Timber.v("## FileService: Uploading file")
|
||||
Timber.v("## Uploading file")
|
||||
|
||||
fileUploader
|
||||
.uploadFile(encryptedFile, attachment.name, MimeTypes.OctetStream, progressListener)
|
||||
} else {
|
||||
Timber.v("## FileService: Clear file")
|
||||
Timber.v("## Clear file")
|
||||
encryptedFile = null
|
||||
fileUploader
|
||||
.uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener)
|
||||
}
|
||||
|
||||
Timber.v("## FileService: Update cache storage for ${contentUploadResponse.contentUri}")
|
||||
Timber.v("## Update cache storage for ${contentUploadResponse.contentUri}")
|
||||
try {
|
||||
fileService.storeDataFor(
|
||||
mxcUrl = contentUploadResponse.contentUri,
|
||||
|
@ -212,11 +260,13 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
originalFile = workingFile,
|
||||
encryptedFile = encryptedFile
|
||||
)
|
||||
Timber.v("## FileService: cache storage updated")
|
||||
Timber.v("## cache storage updated")
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## FileService: Failed to update file cache")
|
||||
Timber.e(failure, "## Failed to update file cache")
|
||||
}
|
||||
|
||||
val uploadThumbnailResult = dealWithThumbnail(params)
|
||||
|
||||
handleSuccess(params,
|
||||
contentUploadResponse.contentUri,
|
||||
uploadedFileEncryptedFileInfo,
|
||||
|
@ -224,12 +274,12 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
uploadThumbnailResult?.uploadedThumbnailEncryptedFileInfo,
|
||||
newAttachmentAttributes)
|
||||
} catch (t: Throwable) {
|
||||
Timber.e(t, "## FileService: ERROR ${t.localizedMessage}")
|
||||
Timber.e(t, "## ERROR ${t.localizedMessage}")
|
||||
handleFailure(params, t)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## FileService: ERROR")
|
||||
return handleFailure(params, e)
|
||||
Timber.e(e, "## ERROR")
|
||||
handleFailure(params, e)
|
||||
} finally {
|
||||
// Delete all temporary files
|
||||
filesToDelete.forEach {
|
||||
|
@ -260,19 +310,23 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
Timber.v("Encrypt thumbnail")
|
||||
notifyTracker(params) { contentUploadStateTracker.setEncryptingThumbnail(it) }
|
||||
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream(), thumbnailData.mimeType)
|
||||
val contentUploadResponse = fileUploader.uploadByteArray(encryptionResult.encryptedByteArray,
|
||||
"thumb_${params.attachment.name}",
|
||||
MimeTypes.OctetStream,
|
||||
thumbnailProgressListener)
|
||||
val contentUploadResponse = fileUploader.uploadByteArray(
|
||||
byteArray = encryptionResult.encryptedByteArray,
|
||||
filename = "thumb_${params.attachment.name}",
|
||||
mimeType = MimeTypes.OctetStream,
|
||||
progressListener = thumbnailProgressListener
|
||||
)
|
||||
UploadThumbnailResult(
|
||||
contentUploadResponse.contentUri,
|
||||
encryptionResult.encryptedFileInfo
|
||||
)
|
||||
} else {
|
||||
val contentUploadResponse = fileUploader.uploadByteArray(thumbnailData.bytes,
|
||||
"thumb_${params.attachment.name}",
|
||||
thumbnailData.mimeType,
|
||||
thumbnailProgressListener)
|
||||
val contentUploadResponse = fileUploader.uploadByteArray(
|
||||
byteArray = thumbnailData.bytes,
|
||||
filename = "thumb_${params.attachment.name}",
|
||||
mimeType = thumbnailData.mimeType,
|
||||
progressListener = thumbnailProgressListener
|
||||
)
|
||||
UploadThumbnailResult(
|
||||
contentUploadResponse.contentUri,
|
||||
null
|
||||
|
@ -291,7 +345,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
return Result.success(
|
||||
WorkerParamsFactory.toData(
|
||||
params.copy(
|
||||
lastFailureMessage = failure.localizedMessage
|
||||
lastFailureMessage = failure.toMatrixErrorStr()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -328,8 +382,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
val messageContent: MessageContent? = event.asDomain().content.toModel()
|
||||
val updatedContent = when (messageContent) {
|
||||
is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes)
|
||||
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo,
|
||||
newAttachmentAttributes.newFileSize)
|
||||
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newAttachmentAttributes)
|
||||
is MessageFileContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes.newFileSize)
|
||||
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes.newFileSize)
|
||||
else -> messageContent
|
||||
|
@ -351,7 +404,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
info = info?.copy(
|
||||
width = newAttachmentAttributes?.newWidth ?: info.width,
|
||||
height = newAttachmentAttributes?.newHeight ?: info.height,
|
||||
size = newAttachmentAttributes?.newFileSize?.toInt() ?: info.size
|
||||
size = newAttachmentAttributes?.newFileSize ?: info.size
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -360,14 +413,16 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
encryptedFileInfo: EncryptedFileInfo?,
|
||||
thumbnailUrl: String?,
|
||||
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
|
||||
size: Long): MessageVideoContent {
|
||||
newAttachmentAttributes: NewAttachmentAttributes?): MessageVideoContent {
|
||||
return copy(
|
||||
url = if (encryptedFileInfo == null) url else null,
|
||||
encryptedFileInfo = encryptedFileInfo?.copy(url = url),
|
||||
videoInfo = videoInfo?.copy(
|
||||
thumbnailUrl = if (thumbnailEncryptedFileInfo == null) thumbnailUrl else null,
|
||||
thumbnailFile = thumbnailEncryptedFileInfo?.copy(url = thumbnailUrl),
|
||||
size = size
|
||||
width = newAttachmentAttributes?.newWidth ?: videoInfo.width,
|
||||
height = newAttachmentAttributes?.newHeight ?: videoInfo.height,
|
||||
size = newAttachmentAttributes?.newFileSize ?: videoInfo.size
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 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.content
|
||||
|
||||
import java.io.File
|
||||
|
||||
internal sealed class VideoCompressionResult {
|
||||
data class Success(val compressedFile: File) : VideoCompressionResult()
|
||||
object CompressionNotNeeded : VideoCompressionResult()
|
||||
object CompressionCancelled : VideoCompressionResult()
|
||||
data class CompressionFailed(val failure: Throwable) : VideoCompressionResult()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue