mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Merge branch 'release/1.5.12' into main
This commit is contained in:
commit
cf1a115c67
277 changed files with 5866 additions and 1427 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1 +1,2 @@
|
|||
**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
|
||||
**/src/androidTest/assets/*.realm filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
4
.github/workflows/danger.yml
vendored
4
.github/workflows/danger.yml
vendored
|
@ -11,9 +11,9 @@ jobs:
|
|||
- run: |
|
||||
npm install --save-dev @babel/plugin-transform-flow-strip-types
|
||||
- name: Danger
|
||||
uses: danger/danger-js@11.1.4
|
||||
uses: danger/danger-js@11.2.0
|
||||
with:
|
||||
args: "--dangerfile tools/danger/dangerfile.js"
|
||||
args: "--dangerfile ./tools/danger/dangerfile.js"
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
# Fallback for forks
|
||||
|
|
4
.github/workflows/quality.yml
vendored
4
.github/workflows/quality.yml
vendored
|
@ -66,9 +66,9 @@ jobs:
|
|||
yarn add danger-plugin-lint-report --dev
|
||||
- name: Danger lint
|
||||
if: always()
|
||||
uses: danger/danger-js@11.1.4
|
||||
uses: danger/danger-js@11.2.0
|
||||
with:
|
||||
args: "--dangerfile tools/danger/dangerfile-lint.js"
|
||||
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
# Fallback for forks
|
||||
|
|
19
.github/workflows/triage-labelled.yml
vendored
19
.github/workflows/triage-labelled.yml
vendored
|
@ -17,7 +17,8 @@ jobs:
|
|||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags')
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
|
@ -88,7 +89,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc0sUA"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
add_product_issues:
|
||||
|
@ -112,7 +113,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAg6N"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
delight_issues_to_board:
|
||||
|
@ -138,7 +139,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc1HvQ"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_voice-message_issues:
|
||||
|
@ -163,7 +164,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc2KCw"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc2KCw"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
move_message_bubbles_issues:
|
||||
name: A-Message-Bubbles to Message bubbles board
|
||||
|
@ -187,7 +188,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc3m-g"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc3m-g"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_ftue_issues:
|
||||
|
@ -212,7 +213,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAqVx"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAqVx"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_WTF_issues:
|
||||
|
@ -237,7 +238,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AArk0"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AArk0"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_element_x_issues:
|
||||
|
@ -267,7 +268,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4ABTXY"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4ABTXY"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
ps_features1:
|
||||
|
|
|
@ -69,7 +69,7 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.pull_request.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc0sUA"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
|
||||
TEAM: "design"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
|
@ -138,6 +138,6 @@ jobs:
|
|||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.pull_request.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4AAg6N"
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
|
||||
TEAM: "product"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
[Rich text editor] Fix design and spacing of rich text editor
|
51
CHANGES.md
51
CHANGES.md
|
@ -1,3 +1,54 @@
|
|||
Changes in Element v1.5.12 (2022-12-15)
|
||||
=======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- [Threads] - Threads Labs Flag is enabled by default and forced to be enabled for existing users, but sill can be disabled manually ([#5503](https://github.com/vector-im/element-android/issues/5503))
|
||||
- [Session manager] Add action to signout all the other session ([#7693](https://github.com/vector-im/element-android/issues/7693))
|
||||
- Remind unverified sessions with a banner once a week ([#7694](https://github.com/vector-im/element-android/issues/7694))
|
||||
- [Session manager] Add actions to rename and signout current session ([#7697](https://github.com/vector-im/element-android/issues/7697))
|
||||
- Voice Broadcast - Update last message in the room list ([#7719](https://github.com/vector-im/element-android/issues/7719))
|
||||
- Delete unused client information from account data ([#7754](https://github.com/vector-im/element-android/issues/7754))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Fix bad pills color background. For light and dark theme the color is now 61708B (iso EleWeb) ([#7274](https://github.com/vector-im/element-android/issues/7274))
|
||||
- [Notifications] Fixed a bug when push notification was automatically dismissed while app is on background ([#7643](https://github.com/vector-im/element-android/issues/7643))
|
||||
- ANR when asking to select the notification method ([#7653](https://github.com/vector-im/element-android/issues/7653))
|
||||
- [Rich text editor] Fix design and spacing of rich text editor ([#7658](https://github.com/vector-im/element-android/issues/7658))
|
||||
- [Rich text editor] Fix keyboard closing after collapsing editor ([#7659](https://github.com/vector-im/element-android/issues/7659))
|
||||
- Rich Text Editor: fix several issues related to insets:
|
||||
* Empty space displayed at the bottom when you don't have permissions to send messages into a room.
|
||||
* Wrong insets being kept when you exit the room screen and the keyboard is displayed, then come back to it. ([#7680](https://github.com/vector-im/element-android/issues/7680))
|
||||
- Fix crash in message composer when room is missing ([#7683](https://github.com/vector-im/element-android/issues/7683))
|
||||
- Fix crash when invalid homeserver url is entered. ([#7684](https://github.com/vector-im/element-android/issues/7684))
|
||||
- Rich Text Editor: improve performance when entering reply/edit/quote mode. ([#7691](https://github.com/vector-im/element-android/issues/7691))
|
||||
- [Rich text editor] Add error tracking for rich text editor ([#7695](https://github.com/vector-im/element-android/issues/7695))
|
||||
- Fix E2EE set up failure whilst signing in using QR code ([#7699](https://github.com/vector-im/element-android/issues/7699))
|
||||
- Fix usage of unknown shield in room summary ([#7710](https://github.com/vector-im/element-android/issues/7710))
|
||||
- Fix crash when the network is not available. ([#7725](https://github.com/vector-im/element-android/issues/7725))
|
||||
- [Session manager] Sessions without encryption support should not prompt to verify ([#7733](https://github.com/vector-im/element-android/issues/7733))
|
||||
- Fix issue of Scan QR code button sometimes not showing when it should be available ([#7737](https://github.com/vector-im/element-android/issues/7737))
|
||||
- Verification request is not showing when verify session popup is displayed ([#7743](https://github.com/vector-im/element-android/issues/7743))
|
||||
- Fix crash when inviting by email. ([#7744](https://github.com/vector-im/element-android/issues/7744))
|
||||
- Revert usage of stable fields in live location sharing and polls ([#7751](https://github.com/vector-im/element-android/issues/7751))
|
||||
- [Poll] Poll end event is not recognized ([#7753](https://github.com/vector-im/element-android/issues/7753))
|
||||
- [Push Notifications] When push notification for threaded message is clicked, thread timeline will be opened instead of room's main timeline ([#7770](https://github.com/vector-im/element-android/issues/7770))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- [Threads] - added API to fetch threads list from the server instead of building it locally from events ([#5819](https://github.com/vector-im/element-android/issues/5819))
|
||||
- Add Z-Labs label for rich text editor and migrate to new label naming. ([#7477](https://github.com/vector-im/element-android/issues/7477))
|
||||
- Crypto database migration tests ([#7645](https://github.com/vector-im/element-android/issues/7645))
|
||||
- Add tracing Id for to device messages ([#7708](https://github.com/vector-im/element-android/issues/7708))
|
||||
- Disable nightly popup and add an entry point in the advanced settings instead. ([#7723](https://github.com/vector-im/element-android/issues/7723))
|
||||
- Save m.local_notification_settings.<device-id> event in account_data ([#7596](https://github.com/vector-im/element-android/issues/7596))
|
||||
- Update notifications setting when m.local_notification_settings.<device-id> event changes for current device ([#7632](https://github.com/vector-im/element-android/issues/7632))
|
||||
|
||||
SDK API changes ⚠️
|
||||
------------------
|
||||
- Handle account data removal ([#7740](https://github.com/vector-im/element-android/issues/7740))
|
||||
|
||||
Changes in Element 1.5.11 (2022-12-07)
|
||||
======================================
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ buildscript {
|
|||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.5.0.2730'
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
|
||||
classpath "com.likethesalad.android:stem-plugin:2.2.3"
|
||||
classpath 'org.owasp:dependency-check-gradle:7.3.0'
|
||||
classpath 'org.owasp:dependency-check-gradle:7.4.1'
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
|
||||
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
||||
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
|
||||
|
@ -45,10 +45,10 @@ plugins {
|
|||
// Detekt
|
||||
id "io.gitlab.arturbosch.detekt" version "1.22.0"
|
||||
// Ksp
|
||||
id "com.google.devtools.ksp" version "1.7.21-1.0.8"
|
||||
id "com.google.devtools.ksp" version "1.7.22-1.0.8"
|
||||
|
||||
// Dependency Analysis
|
||||
id 'com.autonomousapps.dependency-analysis' version "1.16.0"
|
||||
id 'com.autonomousapps.dependency-analysis' version "1.17.0"
|
||||
// Gradle doctor
|
||||
id "com.osacky.doctor" version "0.8.1"
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ ext.versions = [
|
|||
|
||||
def gradle = "7.3.1"
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
def kotlin = "1.7.21"
|
||||
def kotlin = "1.7.22"
|
||||
def kotlinCoroutines = "1.6.4"
|
||||
def dagger = "2.44.2"
|
||||
def appDistribution = "16.0.0-beta05"
|
||||
|
@ -17,7 +17,7 @@ def markwon = "4.6.2"
|
|||
def moshi = "1.14.0"
|
||||
def lifecycle = "2.5.1"
|
||||
def flowBinding = "1.2.0"
|
||||
def flipper = "0.174.0"
|
||||
def flipper = "0.176.0"
|
||||
def epoxy = "5.0.0"
|
||||
def mavericks = "3.0.1"
|
||||
def glide = "4.14.2"
|
||||
|
@ -26,8 +26,8 @@ def jjwt = "0.11.5"
|
|||
// Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert
|
||||
// the whole commit which set version 0.16.0-SNAPSHOT
|
||||
def vanniktechEmoji = "0.16.0-SNAPSHOT"
|
||||
def sentry = "6.7.0"
|
||||
def fragment = "1.5.4"
|
||||
def sentry = "6.9.0"
|
||||
def fragment = "1.5.5"
|
||||
// Testing
|
||||
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
|
||||
def espresso = "3.4.0"
|
||||
|
@ -98,7 +98,7 @@ ext.libs = [
|
|||
],
|
||||
element : [
|
||||
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
||||
'wysiwyg' : "io.element.android:wysiwyg:0.7.0.1"
|
||||
'wysiwyg' : "io.element.android:wysiwyg:0.9.0"
|
||||
],
|
||||
squareup : [
|
||||
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
||||
|
|
55
docs/database_migration_test.md
Normal file
55
docs/database_migration_test.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
<!--- TOC -->
|
||||
|
||||
* [Testing database migration](#testing-database-migration)
|
||||
* [Creating a reference database](#creating-a-reference-database)
|
||||
* [Testing](#testing)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Testing database migration
|
||||
|
||||
### Creating a reference database
|
||||
|
||||
Databases are encrypted, the key to decrypt is needed to setup the test.
|
||||
A special build property must be enabled to extract it.
|
||||
|
||||
Set `vector.debugPrivateData=true` in `~/.gradle/gradle.properties` (to avoid committing by mistake)
|
||||
|
||||
Launch the app in your emulator, login and use the app to fill up the database.
|
||||
|
||||
Save the key for the tested database
|
||||
```
|
||||
RealmKeysUtils W Database key for alias `session_db_fe9f212a611ccf6dea1141777065ed0a`: 935a6dfa0b0fc5cce1414194ed190....
|
||||
RealmKeysUtils W Database key for alias `crypto_module_fe9f212a611ccf6dea1141777065ed0a`: 7b9a21a8a311e85d75b069a343.....
|
||||
```
|
||||
|
||||
|
||||
Use the [Device File Explorer](https://developer.android.com/studio/debug/device-file-explorer) to extrat the database file from the emulator.
|
||||
|
||||
Go to `data/data/im.vector.app.debug/files/<hash>/`
|
||||
Pick the database you want to test (name can be found in SessionRealmConfigurationFactory):
|
||||
- crypto_store.realm for crypto
|
||||
- disk_store.realm for session
|
||||
- etc...
|
||||
|
||||
Download the file on your disk
|
||||
|
||||
### Testing
|
||||
|
||||
Copy the file in `src/AndroidTest/assets`
|
||||
|
||||
see `CryptoSanityMigrationTest` or `RealmSessionStoreMigration43Test` for sample tests.
|
||||
|
||||
There are already some databases in the assets folder.
|
||||
The existing test will properly detect schema changes, and fail with such errors if a migration is missing:
|
||||
|
||||
```
|
||||
io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
|
||||
- Property 'CryptoMetadataEntity.foo' has been added.
|
||||
```
|
||||
|
||||
If you want to test properly more complex database migration (dynamic transforms) ensure that the database contains
|
||||
the entity you want to migrate.
|
||||
|
||||
You can explore the database with [realm studio](https://www.mongodb.com/docs/realm/studio/) if needed.
|
||||
|
2
fastlane/metadata/android/cs-CZ/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Nová implementace celoobrazovkového režimu pro editor formátovaného textu a opravy chyb.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/de-DE/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/de-DE/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Die wichtigsten Änderungen in dieser Version: Der Vollbildmodus des Textverarbeitungseditors wurde neu umgesetzt und es wurden diverse Fehler behoben.
|
||||
Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/en-US/changelogs/40105120.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40105120.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Thread are now enabled by default.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/et/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: tekstitoimeti täisekraanivaade ja erinevate vigade parandused.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/id/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Penerapan baru mode layar penuh untuk Penyunting Teks Kaya dan perbaikan kutu.
|
||||
Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/sk/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hlavné zmeny v tejto verzii: Nová implementácia celo-obrazovkového režimu pre Rozšírený textový editor a opravy chýb.
|
||||
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/sq/changelogs/40105080.txt
Normal file
2
fastlane/metadata/android/sq/changelogs/40105080.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: ndreqje të metash dhe përmirësime.
|
||||
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/sv-SE/changelogs/40105080.txt
Normal file
2
fastlane/metadata/android/sv-SE/changelogs/40105080.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: buggfixar och förbättringar.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/uk/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни в цій версії: Нова реалізація повноекранного режиму для редактора розширеного тексту та виправлення помилок.
|
||||
Перелік усіх змін: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/zh-TW/changelogs/40105100.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40105100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:格式化文字編輯器的全螢幕模式新實作與臭蟲修復。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=db9c8211ed63f61f60292c69e80d89196f9eb36665e369e7f00ac4cc841c2219
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
distributionSha256Sum=312eb12875e1747e05c2f81a4789902d7e4ec5defbd1eefeaccc08acf096505d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
12
gradlew
vendored
12
gradlew
vendored
|
@ -55,7 +55,7 @@
|
|||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
@ -80,10 +80,10 @@ do
|
|||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
@ -143,12 +143,16 @@ fi
|
|||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
|
|
1
gradlew.bat
vendored
1
gradlew.bat
vendored
|
@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
|
|||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
|
|
@ -2789,7 +2789,7 @@
|
|||
<string name="key_authenticity_not_guaranteed">Pravost této šifrované zprávy nelze v tomto zařízení zaručit.</string>
|
||||
<string name="settings_security_incognito_keyboard_summary">Požadujte, aby klávesnice neaktualizovala žádné personalizované údaje, jako je historie psaní a slovník, na základě toho, co jste napsali v konverzacích. Upozorňujeme, že některé klávesnice nemusí toto nastavení respektovat.</string>
|
||||
<string name="settings_security_incognito_keyboard_title">Inkognito klávesnice</string>
|
||||
<string name="command_description_table_flip">Přidá znaky (╯°□°)╯︵ ┻━┻ před zprávy ve formátu obyčejného textu</string>
|
||||
<string name="command_description_table_flip">Přidá znaky (╯°□°)╯︵ ┻━┻ před zprávy ve formátu prostého textu</string>
|
||||
<string name="attachment_type_voice_broadcast">Hlasové vysílání</string>
|
||||
<string name="command_description_devtools">Otevřít nástroje pro vývojáře</string>
|
||||
<string name="room_settings_global_block_unverified_info_text">🔒 V nastavení zabezpečení jste povolili šifrování pouze do ověřených relací pro všechny místnosti.</string>
|
||||
|
@ -2824,8 +2824,8 @@
|
|||
<string name="permissions_rationale_msg_notification">${app_name} potřebuje oprávnění k zobrazování oznámení. Oznámení mohou zobrazovat vaše zprávy, pozvánky atd.
|
||||
\n
|
||||
\nPro zobrazování oznámení povolte přístup na dalších vyskakovacích oknech.</string>
|
||||
<string name="labs_enable_rich_text_editor_summary">Vyzkoušejte rozšířený textový editor (textový režim již brzy)</string>
|
||||
<string name="labs_enable_rich_text_editor_title">Povolit rozšířený textový editor</string>
|
||||
<string name="labs_enable_rich_text_editor_summary">Vyzkoušejte editor formátovaného textu (režim prostého textu již brzy)</string>
|
||||
<string name="labs_enable_rich_text_editor_title">Povolit editor formátovaného textu</string>
|
||||
<string name="qr_code_login_confirm_security_code_description">Ujistěte se, že znáte původ tohoto kódu. Propojením zařízení poskytnete někomu plný přístup ke svému účtu.</string>
|
||||
<string name="qr_code_login_confirm_security_code">Potvrdit</string>
|
||||
<string name="qr_code_login_try_again">Zkuste to znovu</string>
|
||||
|
@ -2868,7 +2868,7 @@
|
|||
<string name="qr_code_login_header_failed_other_device_already_signed_in_description">Druhé zařízení je již přihlášeno.</string>
|
||||
<string name="qr_code_login_header_failed_e2ee_security_issue_description">Při nastavování zabezpečeného zasílání zpráv se vyskytl problém se zabezpečením. Může být napadena jedna z následujících věcí: váš domovský server; vaše internetové připojení; vaše zařízení;</string>
|
||||
<string name="qr_code_login_header_failed_other_description">Žádost se nezdařila.</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Ukládání do vyrovnávací paměti</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Ukládání do vyrovnávací paměti…</string>
|
||||
<string name="a11y_pause_voice_broadcast">Pozastavit hlasové vysílání</string>
|
||||
<string name="a11y_play_voice_broadcast">Přehrát nebo obnovit hlasové vysílání</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Ukončit záznam hlasového vysílání</string>
|
||||
|
@ -2922,4 +2922,6 @@
|
|||
<string name="quoting">Citace</string>
|
||||
<string name="replying_to">Odpovídám na %s</string>
|
||||
<string name="editing">Úpravy</string>
|
||||
<string name="settings_enable_direct_share_summary">Zobrazit poslední chaty v nabídce sdílení systému</string>
|
||||
<string name="settings_enable_direct_share_title">Povolit přímé sdílení</string>
|
||||
</resources>
|
|
@ -2815,7 +2815,7 @@
|
|||
<string name="qr_code_login_header_failed_other_description">Die Anfrage ist fehlgeschlagen.</string>
|
||||
<string name="a11y_play_voice_broadcast">Abspielen oder fortsetzen der Sprachübertragung</string>
|
||||
<string name="a11y_resume_voice_broadcast_record">Fortsetzen der Sprachübertragung</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Puffere</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Puffere …</string>
|
||||
<string name="a11y_pause_voice_broadcast">Pausiere Sprachübertragung</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Stoppe Aufzeichnung der Sprachübertragung</string>
|
||||
<string name="a11y_pause_voice_broadcast_record">Pausiere Aufzeichnung der Sprachübertragung</string>
|
||||
|
@ -2865,4 +2865,6 @@
|
|||
<string name="replying_to">%s antworten</string>
|
||||
<string name="device_manager_other_sessions_hide_ip_address">IP-Adresse ausblenden</string>
|
||||
<string name="device_manager_other_sessions_show_ip_address">IP-Adresse anzeigen</string>
|
||||
<string name="settings_enable_direct_share_summary">Kürzliche Unterhaltungen im Teilen-Menü des Systems anzeigen</string>
|
||||
<string name="settings_enable_direct_share_title">Direktes Teilen aktivieren</string>
|
||||
</resources>
|
|
@ -2805,7 +2805,7 @@
|
|||
<string name="qr_code_login_header_failed_other_device_already_signed_in_description">Teine seade on juba võrku loginud.</string>
|
||||
<string name="qr_code_login_header_failed_e2ee_security_issue_description">Turvalise sõnumivahetuse ülesseadmisel tekkis turvaviga. Üks kolmest võib olla sattunud vale osapoole kontrolli alla: sinu koduserver, sinu internetiühendus või sinu seade;</string>
|
||||
<string name="qr_code_login_header_failed_other_description">Päring ei õnnestunud.</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Andmed on puhverdamisel</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Andmed on puhverdamisel…</string>
|
||||
<string name="a11y_play_voice_broadcast">Alusta või jätka ringhäälingukõne esitamist</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Lõpeta ringhäälingukõne salvestamine</string>
|
||||
<string name="a11y_pause_voice_broadcast_record">Peata ringhäälingukõne salvestamine</string>
|
||||
|
@ -2857,4 +2857,6 @@
|
|||
<string name="message_reply_to_sender_sent_video">saatis video.</string>
|
||||
<string name="message_reply_to_sender_sent_sticker">saatis kleepsu.</string>
|
||||
<string name="message_reply_to_sender_created_poll">koostas küsitluse.</string>
|
||||
<string name="settings_enable_direct_share_title">Kasuta otsejagamist</string>
|
||||
<string name="settings_enable_direct_share_summary">Näita viimaseid vestlusi süsteemses jagamisvaates</string>
|
||||
</resources>
|
|
@ -943,7 +943,7 @@
|
|||
\n
|
||||
\nپیامهایتان با قفلهایی امن شدهاند و فقط شما و گیرندگان دیگر، کلیدهای یکتا را برای قفلگشاییشان دارید.</string>
|
||||
<string name="room_profile_section_security">امنیت</string>
|
||||
<string name="room_profile_section_security_learn_more">بثیشتر بدانید</string>
|
||||
<string name="room_profile_section_security_learn_more">بیشتر بدانید</string>
|
||||
<string name="room_profile_section_more">بیشتر</string>
|
||||
<string name="room_profile_section_admin">کنشهای مدیر</string>
|
||||
<string name="room_profile_section_more_settings">تنظمیات اتاق</string>
|
||||
|
@ -2783,7 +2783,7 @@
|
|||
<string name="attachment_type_selector_poll">نظرسنجیها</string>
|
||||
<string name="attachment_type_selector_file">پیوستها</string>
|
||||
<string name="attachment_type_selector_sticker">برچسبها</string>
|
||||
<string name="a11y_voice_broadcast_buffering">میانگیری</string>
|
||||
<string name="a11y_voice_broadcast_buffering">میانگیری…</string>
|
||||
<string name="voice_broadcast_live">زنده</string>
|
||||
<string name="qr_code_login_confirm_security_code">تأیید</string>
|
||||
<string name="three">۳</string>
|
||||
|
@ -2844,4 +2844,9 @@
|
|||
<string name="quoting">نقل کردن</string>
|
||||
<string name="replying_to">پاسخ دادن به %s</string>
|
||||
<string name="editing">ویرایش کردن</string>
|
||||
<string name="device_manager_sessions_sign_in_with_qr_code_description">میتوانید با یک رمز QR از این افزاره برای ورود به افزارهای همراه یا روی وب استفاده کنید. دو راه برای این کار وجود دارد:</string>
|
||||
<string name="qr_code_login_header_failed_e2ee_security_issue_description">مشکلی امنیتی در برپایی پیامرسانی امن وجود داشت. ممکن است یکی از موارد زیر دستکاری شده باشند: کارساز خانیگیتان؛ اتّصال اینترنتیتان؛ افزاره(های)تان؛</string>
|
||||
<string name="qr_code_login_confirm_security_code_description">لطفاً مطمئن شوید که مبدأ این کد را میدانید. با پیوند دادن افزارهها، دسترسی کامل را به حسابتان میدهید.</string>
|
||||
<string name="settings_enable_direct_share_summary">نمایش گپهای اخیر در فهرست هم رسانی سامانه</string>
|
||||
<string name="settings_enable_direct_share_title">به کار انداختن همرسانی مستقیم</string>
|
||||
</resources>
|
|
@ -2814,7 +2814,7 @@
|
|||
<string name="device_manager_sessions_sign_in_with_qr_code_description">Vous pouvez utiliser cet appareil pour connecter un appareil mobile ou un client web avec un QR code. Il y a deux façons de le faire :</string>
|
||||
<string name="device_manager_sessions_sign_in_with_qr_code_title">Se connecter avec un QR code</string>
|
||||
<string name="login_scan_qr_code">Scanner le QR code</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Mise en mémoire tampon</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Mise en mémoire tampon…</string>
|
||||
<string name="a11y_pause_voice_broadcast">Mettre en pause la diffusion audio</string>
|
||||
<string name="a11y_play_voice_broadcast">Lire ou continuer la diffusion audio</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Arrêter l’enregistrement de la diffusion audio</string>
|
||||
|
@ -2866,4 +2866,6 @@
|
|||
<string name="quoting">Citation de</string>
|
||||
<string name="replying_to">Réponse à %s</string>
|
||||
<string name="editing">Modification</string>
|
||||
<string name="settings_enable_direct_share_summary">Affiche les conversations récentes dans le menu de partage du système</string>
|
||||
<string name="settings_enable_direct_share_title">Activer le partage direct</string>
|
||||
</resources>
|
|
@ -2762,7 +2762,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
|
|||
<string name="qr_code_login_header_failed_other_description">Permintaan gagal.</string>
|
||||
<string name="labs_enable_voice_broadcast_summary">Memungkinkan untuk merekam dan mengirim siaran suara dalam linimasa ruangan.</string>
|
||||
<string name="labs_enable_voice_broadcast_title">Aktifkan siaran suara (dalam pengembangan aktif)</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Memuat</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Memuat…</string>
|
||||
<string name="a11y_pause_voice_broadcast">Jeda siaran suara</string>
|
||||
<string name="a11y_play_voice_broadcast">Mainkan atau lanjutkan siaran suara</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Hentikan rekaman siaran suara</string>
|
||||
|
@ -2812,4 +2812,6 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
|
|||
<string name="editing">Mengedit</string>
|
||||
<string name="device_manager_other_sessions_show_ip_address">Tampilkan alamat IP</string>
|
||||
<string name="replying_to">Membalas ke %s</string>
|
||||
<string name="settings_enable_direct_share_summary">Tampilkan obrolan terkini dalam menu pembagian sistem</string>
|
||||
<string name="settings_enable_direct_share_title">Aktifkan pembagian langsung</string>
|
||||
</resources>
|
|
@ -2805,7 +2805,7 @@
|
|||
<string name="qr_code_login_header_failed_other_device_already_signed_in_description">L\'altro dispositivo ha già fatto l\'accesso.</string>
|
||||
<string name="qr_code_login_header_failed_e2ee_security_issue_description">Si è verificato un problema di sicurezza configurando i messaggi sicuri. Una delle seguenti cose potrebbe essere compromessa: il tuo homeserver; la/e connessione/i internet; il/i dispositivo/i;</string>
|
||||
<string name="qr_code_login_header_failed_other_description">La richiesta è fallita.</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Buffering</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Buffer…</string>
|
||||
<string name="a11y_pause_voice_broadcast">Sospendi trasmissione vocale</string>
|
||||
<string name="a11y_play_voice_broadcast">Avvia o riprendi trasmissione vocale</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Ferma registrazione trasmissione vocale</string>
|
||||
|
@ -2857,4 +2857,6 @@
|
|||
<string name="quoting">Citazione</string>
|
||||
<string name="replying_to">Risposta a %s</string>
|
||||
<string name="editing">Modifica</string>
|
||||
<string name="settings_enable_direct_share_summary">Mostra chat recenti nel menu di condivisione di sistema</string>
|
||||
<string name="settings_enable_direct_share_title">Attiva condivisione diretta</string>
|
||||
</resources>
|
|
@ -2723,7 +2723,7 @@
|
|||
<string name="device_manager_learn_more_sessions_unverified_title">Sessões não-verificadas</string>
|
||||
<string name="device_manager_learn_more_sessions_inactive">Sessões inativas são sessões que você não tem usado em algum tempo, mas elas continuam a receber chaves de encriptação.
|
||||
\n
|
||||
\nRemover sessões inativas melhora segurança e performance, e torna-o mais fácil para você identificar se uma nova sessão é suspeita.</string>
|
||||
\nRemover sessões inativas melhora segurança e performance, e torna mais fácil para você identificar se uma nova sessão é suspeita.</string>
|
||||
<string name="device_manager_learn_more_sessions_inactive_title">Sessões inativas</string>
|
||||
<string name="device_manager_session_rename_warning">Por favor esteja ciente que nomes de sessões também são visíveis a pessoas com quem você se comunica.</string>
|
||||
<string name="device_manager_session_rename_description">Nomes de sessões personalizadas podem ajudar você a reconhecer seus dispositivos mais facilmente.</string>
|
||||
|
@ -2844,9 +2844,9 @@
|
|||
<string name="error_voice_broadcast_unauthorized_title">Não dá pra começar um novo broadcast de voz</string>
|
||||
<string name="a11y_voice_broadcast_fast_forward">Avançar rápido 30 segundos</string>
|
||||
<string name="a11y_voice_broadcast_fast_backward">Retroceder 30 segundos</string>
|
||||
<string name="device_manager_learn_more_sessions_verified_description">Sessões verificadas são onde quer que você está usando esta conta depois de entrar sua frasepasse ou confirmar sua identidade com uma outra sessão verificada.
|
||||
<string name="device_manager_learn_more_sessions_verified_description">Sessões verificadas são onde quer que você esteja usando esta conta depois de entrar sua frasepasse ou confirmar sua identidade com uma outra sessão verificada.
|
||||
\n
|
||||
\nIsto significa que você tem todas as chaves necessitadas para destrancar suas mensagens encriptadas e confirmar a outras(os) usuárias(os) que você confia nesta sessão.</string>
|
||||
\nIsto significa que você tem todas as chaves necessárias para destrancar suas mensagens encriptadas e confirmar a outras(os) usuárias(os) que você confia nesta sessão.</string>
|
||||
<plurals name="device_manager_other_sessions_multi_signout_all">
|
||||
<item quantity="one">Fazer signout de %1$d sessão</item>
|
||||
<item quantity="other">Fazer signout de %1$d sessões</item>
|
||||
|
|
|
@ -2868,7 +2868,7 @@
|
|||
<string name="qr_code_login_header_failed_other_description">Žiadosť zlyhala.</string>
|
||||
<string name="labs_enable_voice_broadcast_summary">Možnosť nahrávania a odosielania hlasového vysielania v časovej osi miestnosti.</string>
|
||||
<string name="labs_enable_voice_broadcast_title">Zapnúť hlasové vysielanie (v štádiu aktívneho vývoja)</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Načítavanie do vyrovnávacej pamäte</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Načítavanie do vyrovnávacej pamäte…</string>
|
||||
<string name="a11y_pause_voice_broadcast">Pozastaviť hlasové vysielanie</string>
|
||||
<string name="a11y_play_voice_broadcast">Prehrať alebo pokračovať v nahrávaní hlasového vysielania</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Zastaviť nahrávanie hlasového vysielania</string>
|
||||
|
@ -2922,4 +2922,6 @@
|
|||
<string name="device_manager_other_sessions_show_ip_address">Zobraziť IP adresu</string>
|
||||
<string name="replying_to">Odpoveď na %s</string>
|
||||
<string name="editing">Úprava</string>
|
||||
<string name="settings_enable_direct_share_summary">Zobraziť posledné konverzácie v systémovej ponuke zdieľania</string>
|
||||
<string name="settings_enable_direct_share_title">Povoliť priame zdieľanie</string>
|
||||
</resources>
|
|
@ -2659,7 +2659,7 @@
|
|||
\nKy shërbyes Home mund të mos jetë formësuar të shfaqë harta.</string>
|
||||
<string name="poll_undisclosed_not_ended">Përfundimet do të jenë të dukshme pasi të ketë përfunduar pyetësori</string>
|
||||
<string name="labs_enable_msc3061_share_history_desc">Kur bëhet ftesë në një dhomë të fshehtëzuar që ka historik ndarjesh me të tjerët, historiku i fshehtëzuar do të jetë i dukshëm.</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Përdo</string>
|
||||
<string name="a11y_voice_broadcast_buffering"></string>
|
||||
<string name="a11y_pause_voice_broadcast">Ndal transmetim zanor</string>
|
||||
<string name="a11y_play_voice_broadcast">Luani ose vazhdoni luajtje transmetimi zanor</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Ndal incizim transmetimi zanor</string>
|
||||
|
@ -2851,4 +2851,6 @@
|
|||
<string name="a11y_voice_broadcast_fast_backward">Kthim prapa 30 sekonda</string>
|
||||
<string name="replying_to">Si përgjigje për %s</string>
|
||||
<string name="labs_enable_deferred_dm_title">Aktivizo MD të lënë për më vonë</string>
|
||||
<string name="a11y_collapse_space_children">Tkurr pjella të %s</string>
|
||||
<string name="a11y_expand_space_children">Zgjero pjella të %s</string>
|
||||
</resources>
|
|
@ -2852,4 +2852,18 @@
|
|||
<string name="error_voice_broadcast_unauthorized_title">Kan inte starta en ny röstsändning</string>
|
||||
<string name="a11y_voice_broadcast_fast_forward">Spola framåt 30 sekunder</string>
|
||||
<string name="a11y_voice_broadcast_fast_backward">Spola tillbaka 30 sekunder</string>
|
||||
<string name="message_reply_to_sender_created_poll">skickade en omröstning.</string>
|
||||
<string name="message_reply_to_sender_sent_sticker">skickade en dekal.</string>
|
||||
<string name="message_reply_to_sender_sent_video">skickade en video.</string>
|
||||
<string name="message_reply_to_sender_sent_image">skickade en bild.</string>
|
||||
<string name="message_reply_to_sender_sent_voice_message">skickade ett röstmeddelande.</string>
|
||||
<string name="message_reply_to_sender_sent_audio_file">skickade en ljudfil.</string>
|
||||
<string name="message_reply_to_sender_sent_file">skickade en fil.</string>
|
||||
<string name="message_reply_to_prefix">Svar på</string>
|
||||
<string name="device_manager_other_sessions_hide_ip_address">Dölj IP-adress</string>
|
||||
<string name="device_manager_other_sessions_show_ip_address">Visa IP-adress</string>
|
||||
<string name="voice_broadcast_recording_time_left">%1$s kvar</string>
|
||||
<string name="quoting">Citerar</string>
|
||||
<string name="replying_to">Besvarar %s</string>
|
||||
<string name="editing">Redigerar</string>
|
||||
</resources>
|
|
@ -2922,7 +2922,7 @@
|
|||
<string name="qr_code_login_header_failed_other_description">Запит не виконаний.</string>
|
||||
<string name="labs_enable_voice_broadcast_summary">Можливість записувати та надсилати голосові трансляції до стрічки кімнати.</string>
|
||||
<string name="labs_enable_voice_broadcast_title">Увімкнути голосові трансляції (в активній розробці)</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Буферизація</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Буферизація…</string>
|
||||
<string name="a11y_pause_voice_broadcast">Призупинити голосову трансляцію</string>
|
||||
<string name="a11y_play_voice_broadcast">Відтворити або поновити відтворення голосової трансляції</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Припинити запис голосової трансляції</string>
|
||||
|
@ -2966,16 +2966,18 @@
|
|||
<string name="device_manager_other_sessions_multi_signout_selection">Вийти</string>
|
||||
<string name="voice_broadcast_recording_time_left">Залишилося %1$s</string>
|
||||
<string name="message_reply_to_sender_sent_audio_file">надсилає аудіофайл.</string>
|
||||
<string name="message_reply_to_sender_sent_file">відправив файл.</string>
|
||||
<string name="message_reply_to_sender_sent_file">надсилає файл.</string>
|
||||
<string name="message_reply_to_prefix">У відповідь на</string>
|
||||
<string name="device_manager_other_sessions_hide_ip_address">Сховати IP-адресу</string>
|
||||
<string name="message_reply_to_sender_created_poll">створив голосування.</string>
|
||||
<string name="message_reply_to_sender_sent_sticker">відправив наліпку.</string>
|
||||
<string name="message_reply_to_sender_sent_video">відправив відео.</string>
|
||||
<string name="message_reply_to_sender_sent_image">відправив зображення.</string>
|
||||
<string name="message_reply_to_sender_sent_voice_message">відправив голосове повідомлення.</string>
|
||||
<string name="message_reply_to_sender_created_poll">створює опитування.</string>
|
||||
<string name="message_reply_to_sender_sent_sticker">надсилає наліпку.</string>
|
||||
<string name="message_reply_to_sender_sent_video">надсилає відео.</string>
|
||||
<string name="message_reply_to_sender_sent_image">надсилає зображення.</string>
|
||||
<string name="message_reply_to_sender_sent_voice_message">надсилає голосове повідомлення.</string>
|
||||
<string name="device_manager_other_sessions_show_ip_address">Показати IP-адресу</string>
|
||||
<string name="quoting">Цитуючи</string>
|
||||
<string name="replying_to">У відповідь на %s</string>
|
||||
<string name="replying_to">У відповідь %s</string>
|
||||
<string name="editing">Редагування</string>
|
||||
<string name="settings_enable_direct_share_summary">Показувати останні бесіди в системному меню загального доступу</string>
|
||||
<string name="settings_enable_direct_share_title">Увімкнути пряме поширення</string>
|
||||
</resources>
|
|
@ -1007,7 +1007,7 @@
|
|||
<string name="settings_discovery_disconnect_with_bound_pid">您当前在身份服务器 %1$s 上共享电子邮件地址或电话号码。您需要重新连接到 %2$s 才能停止共享它们。</string>
|
||||
<string name="settings_agree_to_terms">同意身份服务器 (%s) 服务条款使你可以通过电子邮件地址或电话号码被发现。</string>
|
||||
<string name="labs_allow_extended_logging">启用详细日志。</string>
|
||||
<string name="labs_allow_extended_logging_summary">详细日志将通过在您发送 RageShake 时提供更多日志来帮助开发人员。即使启用,应用程序也不会记录消息内容或任何其他私人数据。</string>
|
||||
<string name="labs_allow_extended_logging_summary">详细日志将通过在您发送愤怒摇动(RageShake)时提供更多日志来帮助开发人员。即使启用,应用程序也不会记录消息内容或任何其他私人数据。</string>
|
||||
<string name="error_terms_not_accepted">接收你的主服务器条款和条件后请重试。</string>
|
||||
<string name="error_network_timeout">服务器似乎响应时间太长,这可能是由于连接不良或服务器错误引起的。请稍后再试。</string>
|
||||
<string name="send_attachment">发送附件</string>
|
||||
|
@ -1205,7 +1205,7 @@
|
|||
<string name="settings_advanced_settings">高级设置</string>
|
||||
<string name="settings_developer_mode">开发者模式</string>
|
||||
<string name="settings_developer_mode_summary">开发者模式激活隐藏的功能,也可能使应用不稳定。仅供开发者使用!</string>
|
||||
<string name="settings_rageshake">摇一摇</string>
|
||||
<string name="settings_rageshake">愤怒摇动(Rageshake)</string>
|
||||
<string name="settings_rageshake_detection_threshold">检测阈值</string>
|
||||
<string name="settings_rageshake_detection_threshold_summary">摇动手机以测试检测阈值</string>
|
||||
<string name="rageshake_detected">检测到摇动!</string>
|
||||
|
@ -1213,7 +1213,7 @@
|
|||
<string name="devices_current_device">当前会话</string>
|
||||
<string name="devices_other_devices">其它会话</string>
|
||||
<string name="autocomplete_limited_results">仅显示第一个结果,请输入更多字符…</string>
|
||||
<string name="settings_developer_mode_fail_fast_title">快速失败</string>
|
||||
<string name="settings_developer_mode_fail_fast_title">快速失败(Fail-fast)</string>
|
||||
<string name="settings_developer_mode_fail_fast_summary">发生意外错误时,${app_name} 可能更经常崩溃</string>
|
||||
<string name="command_description_shrug">在明文消息前添加 ¯\\_(ツ)_/¯</string>
|
||||
<string name="create_room_encryption_title">启用加密</string>
|
||||
|
@ -2694,7 +2694,7 @@
|
|||
<string name="device_manager_verification_status_detail_other_session_unknown">验证您当前的会话以显示此会话的验证状态。</string>
|
||||
<string name="device_manager_verification_status_unknown">未知的验证状态</string>
|
||||
<string name="tooltip_attachment_voice_broadcast">开始语音广播</string>
|
||||
<string name="a11y_voice_broadcast_buffering">缓冲</string>
|
||||
<string name="a11y_voice_broadcast_buffering">正在缓冲……</string>
|
||||
<string name="a11y_pause_voice_broadcast">暂停语音广播</string>
|
||||
<string name="voice_broadcast_live">实时</string>
|
||||
<string name="action_got_it">知道了</string>
|
||||
|
@ -2789,4 +2789,19 @@
|
|||
<plurals name="x_selected">
|
||||
<item quantity="other">已选择 %1$d</item>
|
||||
</plurals>
|
||||
<string name="message_reply_to_sender_created_poll">已创建投票。</string>
|
||||
<string name="message_reply_to_sender_sent_sticker">已发送贴纸。</string>
|
||||
<string name="message_reply_to_sender_sent_video">已发送视频。</string>
|
||||
<string name="message_reply_to_sender_sent_image">已发送图片。</string>
|
||||
<string name="message_reply_to_sender_sent_voice_message">已发送语音消息。</string>
|
||||
<string name="message_reply_to_sender_sent_audio_file">已发送音频文件。</string>
|
||||
<string name="message_reply_to_sender_sent_file">已发送文件。</string>
|
||||
<string name="device_manager_learn_more_sessions_verified_description">已验证的会话是在输入你的口令词组或用另一个已验证的会话确认你的身份之后你使用此账户的任何地方。
|
||||
\n
|
||||
\n这意味着你拥有解锁你的已加密消息和向其他用户证明你信任此会话所需的全部密钥。</string>
|
||||
<plurals name="device_manager_other_sessions_multi_signout_all">
|
||||
<item quantity="other">登出%1$d个会话</item>
|
||||
</plurals>
|
||||
<string name="device_manager_other_sessions_multi_signout_selection">登出</string>
|
||||
<string name="voice_broadcast_recording_time_left">剩余%1$s</string>
|
||||
</resources>
|
|
@ -2760,7 +2760,7 @@
|
|||
<string name="qr_code_login_header_failed_other_description">請求失敗。</string>
|
||||
<string name="labs_enable_voice_broadcast_summary">可以在聊天室時間軸中錄製並傳送語音廣播。</string>
|
||||
<string name="labs_enable_voice_broadcast_title">啟用語音廣播(正在積極開發中)</string>
|
||||
<string name="a11y_voice_broadcast_buffering">正在緩衝</string>
|
||||
<string name="a11y_voice_broadcast_buffering">正在緩衝……</string>
|
||||
<string name="a11y_pause_voice_broadcast">暫停語音廣播</string>
|
||||
<string name="a11y_play_voice_broadcast">播放或繼續語音廣播</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">停止語音廣播錄製</string>
|
||||
|
@ -2810,4 +2810,6 @@
|
|||
<string name="quoting">引用</string>
|
||||
<string name="replying_to">回覆給 %s</string>
|
||||
<string name="editing">正在編輯</string>
|
||||
<string name="settings_enable_direct_share_summary">在系統分享選單中顯示最近聊天</string>
|
||||
<string name="settings_enable_direct_share_title">啟用直接分享</string>
|
||||
</resources>
|
|
@ -134,6 +134,9 @@
|
|||
<string name="notice_crypto_unable_to_decrypt">** Unable to decrypt: %s **</string>
|
||||
<string name="notice_crypto_error_unknown_inbound_session_id">The sender\'s device has not sent us the keys for this message.</string>
|
||||
|
||||
<string name="notice_voice_broadcast_ended">%1$s ended a voice broadcast.</string>
|
||||
<string name="notice_voice_broadcast_ended_by_you">You ended a voice broadcast.</string>
|
||||
|
||||
<!-- Messages -->
|
||||
|
||||
<!-- Room Screen -->
|
||||
|
@ -2487,6 +2490,9 @@
|
|||
<string name="settings_key_requests">Key Requests</string>
|
||||
<string name="settings_export_trail">Export Audit</string>
|
||||
|
||||
<string name="settings_nightly_build">Nightly build</string>
|
||||
<string name="settings_nightly_build_update">Get the latest build (note: you may have trouble to sign in)</string>
|
||||
|
||||
<string name="e2e_use_keybackup">Unlock encrypted messages history</string>
|
||||
|
||||
<string name="refresh">Refresh</string>
|
||||
|
@ -2649,8 +2655,12 @@
|
|||
<string name="unencrypted">Unencrypted</string>
|
||||
<string name="encrypted_unverified">Encrypted by an unverified device</string>
|
||||
<string name="key_authenticity_not_guaranteed">The authenticity of this encrypted message can\'t be guaranteed on this device.</string>
|
||||
<string name="review_logins">Review where you’re logged in</string>
|
||||
<string name="verify_other_sessions">Verify all your sessions to ensure your account & messages are safe</string>
|
||||
<!-- TODO TO BE REMOVED -->
|
||||
<string name="review_logins" tools:ignore="UnusedResources">Review where you’re logged in</string>
|
||||
<!-- TODO TO BE REMOVED -->
|
||||
<string name="verify_other_sessions" tools:ignore="UnusedResources">Verify all your sessions to ensure your account & messages are safe</string>
|
||||
<string name="review_unverified_sessions_title">You have unverified sessions</string>
|
||||
<string name="review_unverified_sessions_description">Review to ensure your account is safe</string>
|
||||
<!-- Argument will be replaced by the other session name (e.g, Desktop, mobile) -->
|
||||
<string name="verify_this_session">Verify the new login accessing your account: %1$s</string>
|
||||
|
||||
|
@ -3025,7 +3035,7 @@
|
|||
|
||||
<string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string>
|
||||
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
||||
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
|
||||
<string name="labs_enable_thread_messages">Enable threaded messages</string>
|
||||
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
|
||||
<string name="settings_show_latest_profile">Show latest user info</string>
|
||||
<string name="settings_show_latest_profile_description">Show the latest profile info (avatar and display name) for all the messages.</string>
|
||||
|
@ -3094,6 +3104,7 @@
|
|||
<string name="audio_message_file_size">(%1$s)</string>
|
||||
|
||||
<string name="voice_broadcast_live">Live</string>
|
||||
<string name="voice_broadcast_live_broadcast">Live broadcast</string>
|
||||
<!-- TODO Rename id to voice_broadcast_buffering -->
|
||||
<string name="a11y_voice_broadcast_buffering">Buffering…</string>
|
||||
<string name="a11y_resume_voice_broadcast_record">Resume voice broadcast record</string>
|
||||
|
@ -3301,6 +3312,7 @@
|
|||
<string name="device_manager_verification_status_detail_current_session_unverified">Verify your current session for enhanced secure messaging.</string>
|
||||
<string name="device_manager_verification_status_detail_other_session_unverified">Verify or sign out from this session for best security and reliability.</string>
|
||||
<string name="device_manager_verification_status_detail_other_session_unknown">Verify your current session to reveal this session\'s verification status.</string>
|
||||
<string name="device_manager_verification_status_detail_session_encryption_not_supported">This session doesn\'t support encryption and thus can\'t be verified.</string>
|
||||
<string name="device_manager_verify_session">Verify Session</string>
|
||||
<string name="device_manager_view_details">View Details</string>
|
||||
<string name="device_manager_other_sessions_view_all">View All (%1$d)</string>
|
||||
|
@ -3359,6 +3371,7 @@
|
|||
<item quantity="one">Sign out of %1$d session</item>
|
||||
<item quantity="other">Sign out of %1$d sessions</item>
|
||||
</plurals>
|
||||
<string name="device_manager_signout_all_other_sessions">Sign out of all other sessions</string>
|
||||
<string name="device_manager_other_sessions_show_ip_address">Show IP address</string>
|
||||
<string name="device_manager_other_sessions_hide_ip_address">Hide IP address</string>
|
||||
<string name="device_manager_session_overview_signout">Sign out of this session</string>
|
||||
|
@ -3392,6 +3405,7 @@
|
|||
<!-- TODO TO BE REMOVED -->
|
||||
<string name="device_manager_learn_more_sessions_verified" tools:ignore="UnusedResources">Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.\n\nThis means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you.</string>
|
||||
<string name="device_manager_learn_more_sessions_verified_description">Verified sessions are anywhere you are using this account after entering your passphrase or confirming your identity with another verified session.\n\nThis means that you have all the keys needed to unlock your encrypted messages and confirm to other users that you trust this session.</string>
|
||||
<string name="device_manager_learn_more_sessions_encryption_not_supported">This session doesn\'t support encryption, so it can\'t be verified.\n\nYou won\'t be able to participate in rooms where encryption is enabled when using this session.\n\nFor best security and privacy, it is recommended to use Matrix clients that support encryption.</string>
|
||||
<string name="device_manager_learn_more_session_rename_title">Renaming sessions</string>
|
||||
<string name="device_manager_learn_more_session_rename">Other users in direct messages and rooms that you join are able to view a full list of your sessions.\n\nThis provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here.</string>
|
||||
<string name="labs_enable_session_manager_title">Enable new session manager</string>
|
||||
|
|
|
@ -44,4 +44,4 @@
|
|||
<color name="palette_black_800">#15191E</color>
|
||||
<color name="palette_black_950">#21262C</color>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<item name="vctr_list_separator">?vctr_content_quinary</item>
|
||||
<item name="vctr_list_separator_system">?vctr_system</item>
|
||||
<item name="vctr_list_separator_on_surface">?vctr_system</item>
|
||||
<item name="vctr_unread_background">?vctr_content_tertiary</item>
|
||||
<item name="vctr_unread_background">?vctr_notice_secondary</item>
|
||||
|
||||
<!-- Material color -->
|
||||
<item name="colorPrimary">@color/element_accent_dark</item>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<item name="vctr_list_separator">?vctr_content_quinary</item>
|
||||
<item name="vctr_list_separator_system">?vctr_system</item>
|
||||
<item name="vctr_list_separator_on_surface">?vctr_system</item>
|
||||
<item name="vctr_unread_background">?vctr_content_tertiary</item>
|
||||
<item name="vctr_unread_background">?vctr_notice_secondary</item>
|
||||
|
||||
<!-- Material color -->
|
||||
<item name="colorPrimary">@color/element_accent_light</item>
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
|
@ -119,13 +118,6 @@ class FlowRoom(private val room: Room) {
|
|||
return room.roomPushRuleService().getLiveRoomNotificationState().asFlow()
|
||||
}
|
||||
|
||||
fun liveThreadSummaries(): Flow<List<ThreadSummary>> {
|
||||
return room.threadsService().getAllThreadSummariesLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
room.threadsService().getAllThreadSummaries()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveThreadList(): Flow<List<ThreadRootEvent>> {
|
||||
return room.threadsLocalService().getAllThreadsLive().asFlow()
|
||||
.startWith(room.coroutineDispatchers.io) {
|
||||
|
|
|
@ -62,7 +62,7 @@ android {
|
|||
// that the app's state is completely cleared between tests.
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.5.11\""
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.5.12\""
|
||||
|
||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7acd69f37612bab0a1ab7f456656712d7ba19dbb679f81b97b58ef44e239f42
|
||||
size 8523776
|
Binary file not shown.
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.common
|
||||
|
||||
import org.matrix.android.sdk.api.RoomDisplayNameFallbackProvider
|
||||
import org.matrix.android.sdk.api.provider.RoomDisplayNameFallbackProvider
|
||||
|
||||
class TestRoomDisplayNameFallbackProvider : RoomDisplayNameFallbackProvider {
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import io.realm.Realm
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
|
||||
class CryptoSanityMigrationTest {
|
||||
@get:Rule val configurationFactory = TestRealmConfigurationFactory()
|
||||
|
||||
lateinit var context: Context
|
||||
var realm: Realm? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = InstrumentationRegistry.getInstrumentation().context
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
realm?.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cryptoDatabaseShouldMigrateGracefully() {
|
||||
val realmName = "crypto_store_20.realm"
|
||||
val migration = RealmCryptoStoreMigration(object : Clock {
|
||||
override fun epochMillis(): Long {
|
||||
return 0L
|
||||
}
|
||||
})
|
||||
val realmConfiguration = configurationFactory.createConfiguration(
|
||||
realmName,
|
||||
"7b9a21a8a311e85d75b069a343c23fc952fc3fec5e0c83ecfa13f24b787479c487c3ed587db3dd1f5805d52041fc0ac246516e94b27ffa699ff928622e621aca",
|
||||
RealmCryptoStoreModule(),
|
||||
migration.schemaVersion,
|
||||
migration
|
||||
)
|
||||
configurationFactory.copyRealmFromAssets(context, realmName, realmName)
|
||||
|
||||
realm = Realm.getInstance(realmConfiguration)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@ import okhttp3.ConnectionSpec
|
|||
import okhttp3.Interceptor
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.metrics.MetricPlugin
|
||||
import org.matrix.android.sdk.api.provider.CustomEventTypesProvider
|
||||
import org.matrix.android.sdk.api.provider.MatrixItemDisplayNameFallbackProvider
|
||||
import org.matrix.android.sdk.api.provider.RoomDisplayNameFallbackProvider
|
||||
import java.net.Proxy
|
||||
|
||||
data class MatrixConfiguration(
|
||||
|
@ -66,7 +69,7 @@ data class MatrixConfiguration(
|
|||
/**
|
||||
* Thread messages default enable/disabled value.
|
||||
*/
|
||||
val threadMessagesEnabledDefault: Boolean = false,
|
||||
val threadMessagesEnabledDefault: Boolean = true,
|
||||
/**
|
||||
* List of network interceptors, they will be added when building an OkHttp client.
|
||||
*/
|
||||
|
@ -75,9 +78,12 @@ data class MatrixConfiguration(
|
|||
* Sync configuration.
|
||||
*/
|
||||
val syncConfig: SyncConfig = SyncConfig(),
|
||||
|
||||
/**
|
||||
* Metrics plugin that can be used to capture metrics from matrix-sdk-android.
|
||||
*/
|
||||
val metricPlugins: List<MetricPlugin> = emptyList()
|
||||
val metricPlugins: List<MetricPlugin> = emptyList(),
|
||||
/**
|
||||
* CustomEventTypesProvider to provide custom event types to the sdk which should be processed with internal events.
|
||||
*/
|
||||
val customEventTypesProvider: CustomEventTypesProvider? = null,
|
||||
)
|
||||
|
|
|
@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LocalNotificationSettingsContent(
|
||||
@Json(name = "is_silenced") val isSilenced: Boolean = false
|
||||
@Json(name = "is_silenced")
|
||||
val isSilenced: Boolean?
|
||||
)
|
||||
|
|
|
@ -125,12 +125,6 @@ interface AuthenticationService {
|
|||
deviceId: String? = null
|
||||
): Session
|
||||
|
||||
/**
|
||||
* @param homeServerConnectionConfig the information about the homeserver and other configuration
|
||||
* Return true if qr code login is supported by the server, false otherwise.
|
||||
*/
|
||||
suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
|
||||
|
||||
/**
|
||||
* Authenticate using m.login.token method during sign in with QR code.
|
||||
* @param homeServerConnectionConfig the information about the homeserver and other configuration
|
||||
|
|
|
@ -22,5 +22,6 @@ data class LoginFlowResult(
|
|||
val isLoginAndRegistrationSupported: Boolean,
|
||||
val homeServerUrl: String,
|
||||
val isOutdatedHomeserver: Boolean,
|
||||
val isLogoutDevicesSupported: Boolean
|
||||
val isLogoutDevicesSupported: Boolean,
|
||||
val isLoginWithQrSupported: Boolean,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.provider
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
||||
/**
|
||||
* Provide custom event types which should be processed with the internal event types.
|
||||
*/
|
||||
interface CustomEventTypesProvider {
|
||||
|
||||
/**
|
||||
* Custom event types to include when computing [RoomSummary.latestPreviewableEvent].
|
||||
*/
|
||||
val customPreviewableEventTypes: List<String>
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api
|
||||
package org.matrix.android.sdk.api.provider
|
||||
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api
|
||||
package org.matrix.android.sdk.api.provider
|
||||
|
||||
/**
|
||||
* This interface exists to let the implementation provide localized room display name fallback.
|
|
@ -35,7 +35,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S
|
|||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.util.MatrixJsonParser
|
||||
import org.matrix.android.sdk.api.util.awaitCallback
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
|
@ -147,6 +150,14 @@ class Rendezvous(
|
|||
val deviceKey = crypto.getMyDevice().fingerprint()
|
||||
send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
|
||||
|
||||
try {
|
||||
// explicitly download keys for ourself rather than racing with initial sync which might not complete in time
|
||||
awaitCallback<MXUsersDevicesMap<CryptoDeviceInfo>> { crypto.downloadKeys(listOf(userId), false, it) }
|
||||
} catch (e: Throwable) {
|
||||
// log as warning and continue as initial sync might still complete
|
||||
Timber.tag(TAG).w(e, "Failed to download keys for self")
|
||||
}
|
||||
|
||||
// await confirmation of verification
|
||||
val verificationResponse = receive()
|
||||
if (verificationResponse?.outcome == Outcome.VERIFIED) {
|
||||
|
|
|
@ -63,4 +63,17 @@ interface SessionAccountDataService {
|
|||
* Update the account data with the provided type and the provided account data content.
|
||||
*/
|
||||
suspend fun updateUserAccountData(type: String, content: Content)
|
||||
|
||||
/**
|
||||
* Retrieve user account data list whose type starts with the given type.
|
||||
* @param type the type or the starting part of a type
|
||||
* @return list of account data whose type starts with the given type
|
||||
*/
|
||||
fun getUserAccountDataEventsStartWith(type: String): List<UserAccountDataEvent>
|
||||
|
||||
/**
|
||||
* Deletes user account data of the given type.
|
||||
* @param type the type to delete from user account data
|
||||
*/
|
||||
suspend fun deleteUserAccountData(type: String)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.events.model
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType.MSGTYPE_VERIFICATION_REQUEST
|
||||
|
||||
/**
|
||||
* Constants defining known event types from Matrix specifications.
|
||||
*/
|
||||
|
@ -126,6 +128,7 @@ object EventType {
|
|||
|
||||
fun isVerificationEvent(type: String): Boolean {
|
||||
return when (type) {
|
||||
MSGTYPE_VERIFICATION_REQUEST,
|
||||
KEY_VERIFICATION_START,
|
||||
KEY_VERIFICATION_ACCEPT,
|
||||
KEY_VERIFICATION_KEY,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.threads
|
||||
|
||||
sealed class FetchThreadsResult {
|
||||
data class ShouldFetchMore(val nextBatch: String) : FetchThreadsResult()
|
||||
object ReachedEnd : FetchThreadsResult()
|
||||
object Failed : FetchThreadsResult()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.threads
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class ThreadFilter {
|
||||
@Json(name = "all") ALL,
|
||||
@Json(name = "participated") PARTICIPATED,
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.api.session.room.threads
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.session.room.ResultBoundaries
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
|
||||
data class ThreadLivePageResult(
|
||||
val livePagedList: LiveData<PagedList<ThreadSummary>>,
|
||||
val liveBoundaries: LiveData<ResultBoundaries>
|
||||
)
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.room.threads
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
|
||||
/**
|
||||
|
@ -27,15 +27,14 @@ import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
|||
*/
|
||||
interface ThreadsService {
|
||||
|
||||
/**
|
||||
* Returns a [LiveData] list of all the [ThreadSummary] that exists at the room level.
|
||||
*/
|
||||
fun getAllThreadSummariesLive(): LiveData<List<ThreadSummary>>
|
||||
suspend fun getPagedThreadsList(userParticipating: Boolean, pagedListConfig: PagedList.Config): ThreadLivePageResult
|
||||
|
||||
suspend fun fetchThreadList(nextBatchId: String?, limit: Int, filter: ThreadFilter = ThreadFilter.ALL): FetchThreadsResult
|
||||
|
||||
/**
|
||||
* Returns a list of all the [ThreadSummary] that exists at the room level.
|
||||
*/
|
||||
fun getAllThreadSummaries(): List<ThreadSummary>
|
||||
suspend fun getAllThreadSummaries(): List<ThreadSummary>
|
||||
|
||||
/**
|
||||
* Enhance the provided ThreadSummary[List] by adding the latest
|
||||
|
@ -51,9 +50,4 @@ interface ThreadsService {
|
|||
* @param limit defines the number of max results the api will respond with
|
||||
*/
|
||||
suspend fun fetchThreadTimeline(rootThreadEventId: String, from: String, limit: Int)
|
||||
|
||||
/**
|
||||
* Fetch all thread summaries for the current room using the enhanced /messages api.
|
||||
*/
|
||||
suspend fun fetchThreadSummaries()
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
|||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixIdFailure
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
@ -299,7 +298,8 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||
isLoginAndRegistrationSupported = versions.isLoginAndRegistrationSupportedBySdk(),
|
||||
homeServerUrl = homeServerUrl,
|
||||
isOutdatedHomeserver = !versions.isSupportedBySdk(),
|
||||
isLogoutDevicesSupported = versions.doesServerSupportLogoutDevices()
|
||||
isLogoutDevicesSupported = versions.doesServerSupportLogoutDevices(),
|
||||
isLoginWithQrSupported = versions.doesServerSupportQrCodeLogin(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -408,20 +408,6 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean {
|
||||
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||
val versions = runCatching {
|
||||
executeRequest(null) {
|
||||
authAPI.versions()
|
||||
}
|
||||
}
|
||||
return if (versions.isSuccess) {
|
||||
versions.getOrNull()?.doesServerSupportQrCodeLogin().orFalse()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loginUsingQrLoginToken(
|
||||
homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
loginToken: String,
|
||||
|
|
|
@ -17,14 +17,22 @@
|
|||
package org.matrix.android.sdk.internal.crypto.tasks
|
||||
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
const val TO_DEVICE_TRACING_ID_KEY = "org.matrix.msgid"
|
||||
|
||||
fun Event.toDeviceTracingId(): String? = content?.get(TO_DEVICE_TRACING_ID_KEY) as? String
|
||||
|
||||
internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
||||
data class Params(
|
||||
// the type of event to send
|
||||
|
@ -32,7 +40,9 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
|||
// the content to send. Map from user_id to device_id to content dictionary.
|
||||
val contentMap: MXUsersDevicesMap<Any>,
|
||||
// the transactionId. If not provided, a transactionId will be created by the task
|
||||
val transactionId: String? = null
|
||||
val transactionId: String? = null,
|
||||
// add tracing id, notice that to device events that do signature on content might be broken by it
|
||||
val addTracingIds: Boolean = !EventType.isVerificationEvent(eventType),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -42,15 +52,22 @@ internal class DefaultSendToDeviceTask @Inject constructor(
|
|||
) : SendToDeviceTask {
|
||||
|
||||
override suspend fun execute(params: SendToDeviceTask.Params) {
|
||||
val sendToDeviceBody = SendToDeviceBody(
|
||||
messages = params.contentMap.map
|
||||
)
|
||||
|
||||
// If params.transactionId is not provided, we create a unique txnId.
|
||||
// It's important to do that outside the requestBlock parameter of executeRequest()
|
||||
// to use the same value if the request is retried
|
||||
val txnId = params.transactionId ?: createUniqueTxnId()
|
||||
|
||||
// add id tracing to debug
|
||||
val decorated = if (params.addTracingIds) {
|
||||
decorateWithToDeviceTracingIds(params)
|
||||
} else {
|
||||
params.contentMap.map to emptyList()
|
||||
}
|
||||
|
||||
val sendToDeviceBody = SendToDeviceBody(
|
||||
messages = decorated.first
|
||||
)
|
||||
|
||||
return executeRequest(
|
||||
globalErrorReceiver,
|
||||
canRetry = true,
|
||||
|
@ -61,8 +78,35 @@ internal class DefaultSendToDeviceTask @Inject constructor(
|
|||
transactionId = txnId,
|
||||
body = sendToDeviceBody
|
||||
)
|
||||
Timber.i("Sent to device type=${params.eventType} txnid=$txnId [${decorated.second.joinToString(",")}]")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To make it easier to track down where to-device messages are getting lost,
|
||||
* add a custom property to each one, and that will be logged after sent and on reception. Synapse will also log
|
||||
* this property.
|
||||
* @return A pair, first is the decorated content, and second info to log out after sending
|
||||
*/
|
||||
private fun decorateWithToDeviceTracingIds(params: SendToDeviceTask.Params): Pair<Map<String, Map<String, Any>>, List<String>> {
|
||||
val tracingInfo = mutableListOf<String>()
|
||||
val decoratedContent = params.contentMap.map.map { userToDeviceMap ->
|
||||
val userId = userToDeviceMap.key
|
||||
userId to userToDeviceMap.value.map {
|
||||
val deviceId = it.key
|
||||
deviceId to it.value.toContent().toMutableMap().apply {
|
||||
put(
|
||||
TO_DEVICE_TRACING_ID_KEY,
|
||||
UUID.randomUUID().toString().also {
|
||||
tracingInfo.add("$userId/$deviceId (msgid $it)")
|
||||
}
|
||||
)
|
||||
}
|
||||
}.toMap()
|
||||
}.toMap()
|
||||
|
||||
return decoratedContent to tracingInfo
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createUniqueTxnId() = UUID.randomUUID().toString()
|
||||
|
|
|
@ -62,6 +62,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo042
|
|||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo043
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo044
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo045
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo046
|
||||
import org.matrix.android.sdk.internal.util.Normalizer
|
||||
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
||||
import javax.inject.Inject
|
||||
|
@ -70,7 +71,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
private val normalizer: Normalizer
|
||||
) : MatrixRealmMigration(
|
||||
dbName = "Session",
|
||||
schemaVersion = 45L,
|
||||
schemaVersion = 46L,
|
||||
) {
|
||||
/**
|
||||
* Forces all RealmSessionStoreMigration instances to be equal.
|
||||
|
@ -125,5 +126,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
if (oldVersion < 43) MigrateSessionTo043(realm).perform()
|
||||
if (oldVersion < 44) MigrateSessionTo044(realm).perform()
|
||||
if (oldVersion < 45) MigrateSessionTo045(realm).perform()
|
||||
if (oldVersion < 46) MigrateSessionTo046(realm).perform()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,11 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
|
|||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
|
||||
import org.matrix.android.sdk.internal.database.query.get
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
|
@ -113,16 +115,16 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
userId: String,
|
||||
cryptoService: CryptoService? = null,
|
||||
currentTimeMillis: Long,
|
||||
) {
|
||||
): ThreadSummaryEntity? {
|
||||
when (threadSummaryType) {
|
||||
ThreadSummaryUpdateType.REPLACE -> {
|
||||
rootThreadEvent?.eventId ?: return
|
||||
rootThreadEvent.senderId ?: return
|
||||
rootThreadEvent?.eventId ?: return null
|
||||
rootThreadEvent.senderId ?: return null
|
||||
|
||||
val numberOfThreads = rootThreadEvent.unsignedData?.relations?.latestThread?.count ?: return
|
||||
val numberOfThreads = rootThreadEvent.unsignedData?.relations?.latestThread?.count ?: return null
|
||||
|
||||
// Something is wrong with the server return
|
||||
if (numberOfThreads <= 0) return
|
||||
if (numberOfThreads <= 0) return null
|
||||
|
||||
val threadSummary = ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEvent.eventId).also {
|
||||
Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ")
|
||||
|
@ -153,12 +155,13 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
)
|
||||
|
||||
roomEntity.addIfNecessary(threadSummary)
|
||||
return threadSummary
|
||||
}
|
||||
ThreadSummaryUpdateType.ADD -> {
|
||||
val rootThreadEventId = threadEventEntity?.rootThreadEventId ?: return
|
||||
val rootThreadEventId = threadEventEntity?.rootThreadEventId ?: return null
|
||||
Timber.i("###THREADS ThreadSummaryHelper ADD for root eventId:$rootThreadEventId")
|
||||
|
||||
val threadSummary = ThreadSummaryEntity.getOrNull(realm, roomId, rootThreadEventId)
|
||||
var threadSummary = ThreadSummaryEntity.getOrNull(realm, roomId, rootThreadEventId)
|
||||
if (threadSummary != null) {
|
||||
// ThreadSummary exists so lets add the latest event
|
||||
Timber.i("###THREADS ThreadSummaryHelper ADD root eventId:$rootThreadEventId exists, lets update latest thread event.")
|
||||
|
@ -172,7 +175,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
Timber.i("###THREADS ThreadSummaryHelper ADD root eventId:$rootThreadEventId do not exists, lets try to create one")
|
||||
threadEventEntity.findRootThreadEvent()?.let { rootThreadEventEntity ->
|
||||
// Root thread event entity exists so lets create a new record
|
||||
ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEventEntity.eventId).let {
|
||||
threadSummary = ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEventEntity.eventId).also {
|
||||
it.updateThreadSummary(
|
||||
rootThreadEventEntity = rootThreadEventEntity,
|
||||
numberOfThreads = 1,
|
||||
|
@ -183,7 +186,12 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
|||
roomEntity.addIfNecessary(it)
|
||||
}
|
||||
}
|
||||
|
||||
threadSummary?.let {
|
||||
ThreadListPageEntity.get(realm, roomId)?.threadSummaries?.add(it)
|
||||
}
|
||||
}
|
||||
return threadSummary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.migration
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntityFields
|
||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
||||
|
||||
internal class MigrateSessionTo046(realm: DynamicRealm) : RealmMigrator(realm, 46) {
|
||||
|
||||
override fun doMigrate(realm: DynamicRealm) {
|
||||
realm.schema.create("ThreadListPageEntity")
|
||||
.addField(ThreadListPageEntityFields.ROOM_ID, String::class.java)
|
||||
.addPrimaryKey(ThreadListPageEntityFields.ROOM_ID)
|
||||
.setRequired(ThreadListPageEntityFields.ROOM_ID, true)
|
||||
.addRealmListField(ThreadListPageEntityFields.THREAD_SUMMARIES.`$`, realm.schema.get("ThreadSummaryEntity")!!)
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.database.model
|
|||
import io.realm.annotations.RealmModule
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
|
||||
/**
|
||||
|
@ -72,6 +73,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
|
|||
UserPresenceEntity::class,
|
||||
ThreadSummaryEntity::class,
|
||||
SyncFilterParamsEntity::class,
|
||||
ThreadListPageEntity::class
|
||||
]
|
||||
)
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.model.threads
|
||||
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class ThreadListPageEntity(
|
||||
@PrimaryKey var roomId: String = "",
|
||||
var threadSummaries: RealmList<ThreadSummaryEntity> = RealmList()
|
||||
) : RealmObject() {
|
||||
companion object
|
||||
}
|
|
@ -40,5 +40,8 @@ internal open class ThreadSummaryEntity(
|
|||
@LinkingObjects("threadSummaries")
|
||||
val room: RealmResults<RoomEntity>? = null
|
||||
|
||||
@LinkingObjects("threadSummaries")
|
||||
val page: RealmResults<ThreadListPageEntity>? = null
|
||||
|
||||
companion object
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import io.realm.RealmQuery
|
|||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
||||
|
||||
|
@ -44,3 +45,11 @@ internal fun RoomEntity.Companion.where(realm: Realm, membership: Membership? =
|
|||
internal fun RoomEntity.fastContains(eventId: String): Boolean {
|
||||
return EventEntity.where(realm, eventId = eventId).findFirst() != null
|
||||
}
|
||||
|
||||
internal fun RoomEntity.removeAccountData(type: String) {
|
||||
accountData
|
||||
.where()
|
||||
.equalTo(RoomAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
?.deleteFromRealm()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.createObject
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntityFields
|
||||
|
||||
internal fun ThreadListPageEntity.Companion.get(realm: Realm, roomId: String): ThreadListPageEntity? {
|
||||
return realm.where<ThreadListPageEntity>().equalTo(ThreadListPageEntityFields.ROOM_ID, roomId).findFirst()
|
||||
}
|
||||
|
||||
internal fun ThreadListPageEntity.Companion.getOrCreate(realm: Realm, roomId: String): ThreadListPageEntity {
|
||||
return get(realm, roomId) ?: realm.createObject(roomId)
|
||||
}
|
|
@ -22,6 +22,7 @@ import io.realm.RealmQuery
|
|||
import io.realm.RealmResults
|
||||
import io.realm.Sort
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
|
||||
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
||||
|
@ -94,14 +95,27 @@ internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEvent
|
|||
if (filters.filterTypes && filters.allowedTypes.isNotEmpty()) {
|
||||
beginGroup()
|
||||
filters.allowedTypes.forEachIndexed { index, filter ->
|
||||
if (filter.stateKey == null) {
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
if (filter.eventType == EventType.ENCRYPTED) {
|
||||
val otherTypes = filters.allowedTypes.minus(filter).map { it.eventType }
|
||||
if (filter.stateKey == null) {
|
||||
filterEncryptedTypes(otherTypes)
|
||||
} else {
|
||||
beginGroup()
|
||||
filterEncryptedTypes(otherTypes)
|
||||
and()
|
||||
equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
|
||||
endGroup()
|
||||
}
|
||||
} else {
|
||||
beginGroup()
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
and()
|
||||
equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
|
||||
endGroup()
|
||||
if (filter.stateKey == null) {
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
} else {
|
||||
beginGroup()
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
|
||||
and()
|
||||
equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
|
||||
endGroup()
|
||||
}
|
||||
}
|
||||
if (index != filters.allowedTypes.size - 1) {
|
||||
or()
|
||||
|
@ -115,7 +129,6 @@ internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEvent
|
|||
if (filters.filterEdits) {
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.REFERENCE)
|
||||
}
|
||||
if (filters.filterRedacted) {
|
||||
not().like(TimelineEventEntityFields.ROOT.UNSIGNED_DATA, TimelineEventFilter.Unsigned.REDACTED)
|
||||
|
@ -124,6 +137,21 @@ internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEvent
|
|||
return this
|
||||
}
|
||||
|
||||
internal fun RealmQuery<TimelineEventEntity>.filterEncryptedTypes(allowedTypes: List<String>): RealmQuery<TimelineEventEntity> {
|
||||
beginGroup()
|
||||
equalTo(TimelineEventEntityFields.ROOT.TYPE, EventType.ENCRYPTED)
|
||||
and()
|
||||
beginGroup()
|
||||
isNull(TimelineEventEntityFields.ROOT.DECRYPTION_RESULT_JSON)
|
||||
allowedTypes.forEach { eventType ->
|
||||
or()
|
||||
like(TimelineEventEntityFields.ROOT.DECRYPTION_RESULT_JSON, TimelineEventFilter.DecryptedContent.type(eventType))
|
||||
}
|
||||
endGroup()
|
||||
endGroup()
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<String>): RealmQuery<TimelineEventEntity> {
|
||||
return if (filterTypes.isEmpty()) {
|
||||
this
|
||||
|
|
|
@ -34,6 +34,7 @@ internal object TimelineEventFilter {
|
|||
*/
|
||||
internal object DecryptedContent {
|
||||
internal const val URL = """{*"file":*"url":*}"""
|
||||
fun type(type: String) = """{*"type":*"$type"*}"""
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
|
||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
|
||||
|
||||
/**
|
||||
* Delete an account_data event.
|
||||
*/
|
||||
internal fun UserAccountDataEntity.Companion.delete(realm: Realm, type: String) {
|
||||
realm
|
||||
.where<UserAccountDataEntity>()
|
||||
.equalTo(UserAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
?.deleteFromRealm()
|
||||
}
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBod
|
|||
import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody
|
||||
import org.matrix.android.sdk.internal.session.room.read.ReadBody
|
||||
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.ThreadSummariesResponse
|
||||
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
|
||||
import org.matrix.android.sdk.internal.session.room.send.SendResponse
|
||||
import org.matrix.android.sdk.internal.session.room.tags.TagBody
|
||||
|
@ -427,6 +428,19 @@ internal interface RoomAPI {
|
|||
@Body content: JsonDict
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove an account_data event from the room.
|
||||
* @param userId the user id
|
||||
* @param roomId the room id
|
||||
* @param type the type
|
||||
*/
|
||||
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc3391/user/{userId}/rooms/{roomId}/account_data/{type}")
|
||||
suspend fun deleteRoomAccountData(
|
||||
@Path("userId") userId: String,
|
||||
@Path("roomId") roomId: String,
|
||||
@Path("type") type: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Upgrades the given room to a particular room version.
|
||||
* Errors:
|
||||
|
@ -451,4 +465,12 @@ internal interface RoomAPI {
|
|||
@Path("roomIdOrAlias") roomidOrAlias: String,
|
||||
@Query("via") viaServers: List<String>?
|
||||
): RoomStrippedState
|
||||
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "rooms/{roomId}/threads")
|
||||
suspend fun getThreadsList(
|
||||
@Path("roomId") roomId: String,
|
||||
@Query("include") include: String? = "all",
|
||||
@Query("from") from: String? = null,
|
||||
@Query("limit") limit: Int? = null
|
||||
): ThreadSummariesResponse
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
|||
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
|
||||
import org.matrix.android.sdk.api.session.room.model.VoteInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.VoteSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
|
@ -78,7 +77,7 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro
|
|||
val content = event.getClearContent()?.toModel<MessagePollResponseContent>() ?: return false
|
||||
val roomId = event.roomId ?: return false
|
||||
val senderId = event.senderId ?: return false
|
||||
val targetEventId = (event.getRelationContent() ?: content.relatesTo)?.eventId ?: return false
|
||||
val targetEventId = event.getRelationContent()?.eventId ?: return false
|
||||
val targetPollContent = getPollContent(session, roomId, targetEventId) ?: return false
|
||||
|
||||
val annotationsSummaryEntity = getAnnotationsSummaryEntity(realm, roomId, targetEventId)
|
||||
|
@ -154,9 +153,8 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro
|
|||
}
|
||||
|
||||
override fun handlePollEndEvent(session: Session, powerLevelsHelper: PowerLevelsHelper, realm: Realm, event: Event): Boolean {
|
||||
val content = event.getClearContent()?.toModel<MessageEndPollContent>() ?: return false
|
||||
val roomId = event.roomId ?: return false
|
||||
val pollEventId = content.relatesTo?.eventId ?: return false
|
||||
val pollEventId = event.getRelationContent()?.eventId ?: return false
|
||||
val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId
|
||||
val isPollOwner = pollOwnerId == event.senderId
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ internal class DefaultStartLiveLocationShareTask @Inject constructor(
|
|||
isLive = true,
|
||||
unstableTimestampMillis = clock.epochMillis()
|
||||
).toContent()
|
||||
val eventType = EventType.STATE_ROOM_BEACON_INFO.stable
|
||||
val eventType = EventType.STATE_ROOM_BEACON_INFO.unstable
|
||||
val sendStateTaskParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = userId,
|
||||
|
|
|
@ -45,7 +45,7 @@ internal class DefaultStopLiveLocationShareTask @Inject constructor(
|
|||
val sendStateTaskParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = stateKey,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
body = updatedContent
|
||||
)
|
||||
return try {
|
||||
|
|
|
@ -16,37 +16,38 @@
|
|||
package org.matrix.android.sdk.internal.session.room.relation.threads
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.RealmList
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.threads.FetchThreadsResult
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadFilter
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummaryUpdateType
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.database.helper.createOrUpdate
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.filter.FilterFactory
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/***
|
||||
* This class is responsible to Fetch all the thread in the current room,
|
||||
* To fetch all threads in a room, the /messages API is used with newly added filtering options.
|
||||
*/
|
||||
internal interface FetchThreadSummariesTask : Task<FetchThreadSummariesTask.Params, DefaultFetchThreadSummariesTask.Result> {
|
||||
internal interface FetchThreadSummariesTask : Task<FetchThreadSummariesTask.Params, FetchThreadsResult> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val from: String = "",
|
||||
val limit: Int = 500,
|
||||
val isUserParticipating: Boolean = true
|
||||
val from: String? = null,
|
||||
val limit: Int = 5,
|
||||
val filter: ThreadFilter? = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -59,39 +60,43 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
|||
private val clock: Clock,
|
||||
) : FetchThreadSummariesTask {
|
||||
|
||||
override suspend fun execute(params: FetchThreadSummariesTask.Params): Result {
|
||||
val filter = FilterFactory.createThreadsFilter(
|
||||
numberOfEvents = params.limit,
|
||||
userId = if (params.isUserParticipating) userId else null
|
||||
).toJSONString()
|
||||
|
||||
val response = executeRequest(
|
||||
globalErrorReceiver,
|
||||
canRetry = true
|
||||
) {
|
||||
roomAPI.getRoomMessagesFrom(params.roomId, params.from, PaginationDirection.BACKWARDS.value, params.limit, filter)
|
||||
override suspend fun execute(params: FetchThreadSummariesTask.Params): FetchThreadsResult {
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.getThreadsList(
|
||||
roomId = params.roomId,
|
||||
include = params.filter?.toString()?.lowercase(),
|
||||
from = params.from,
|
||||
limit = params.limit
|
||||
)
|
||||
}
|
||||
|
||||
Timber.i("###THREADS DefaultFetchThreadSummariesTask Fetched size:${response.events.size} nextBatch:${response.end} ")
|
||||
handleResponse(response, params)
|
||||
|
||||
return handleResponse(response, params)
|
||||
return when {
|
||||
response.nextBatch != null -> FetchThreadsResult.ShouldFetchMore(response.nextBatch)
|
||||
else -> FetchThreadsResult.ReachedEnd
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleResponse(
|
||||
response: PaginationResponse,
|
||||
response: ThreadSummariesResponse,
|
||||
params: FetchThreadSummariesTask.Params
|
||||
): Result {
|
||||
val rootThreadList = response.events
|
||||
) {
|
||||
val rootThreadList = response.chunk
|
||||
|
||||
val threadSummaries = RealmList<ThreadSummaryEntity>()
|
||||
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val roomEntity = RoomEntity.where(realm, roomId = params.roomId).findFirst() ?: return@awaitTransaction
|
||||
|
||||
val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>()
|
||||
|
||||
for (rootThreadEvent in rootThreadList) {
|
||||
if (rootThreadEvent.eventId == null || rootThreadEvent.senderId == null || rootThreadEvent.type == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
ThreadSummaryEntity.createOrUpdate(
|
||||
val threadSummary = ThreadSummaryEntity.createOrUpdate(
|
||||
threadSummaryType = ThreadSummaryUpdateType.REPLACE,
|
||||
realm = realm,
|
||||
roomId = params.roomId,
|
||||
|
@ -102,14 +107,16 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
|||
cryptoService = cryptoService,
|
||||
currentTimeMillis = clock.epochMillis(),
|
||||
)
|
||||
|
||||
threadSummaries.add(threadSummary)
|
||||
}
|
||||
|
||||
val page = ThreadListPageEntity.getOrCreate(realm, params.roomId)
|
||||
threadSummaries.forEach {
|
||||
if (!page.threadSummaries.contains(it)) {
|
||||
page.threadSummaries.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.SUCCESS
|
||||
}
|
||||
|
||||
enum class Result {
|
||||
SHOULD_FETCH_MORE,
|
||||
REACHED_END,
|
||||
SUCCESS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.matrix.android.sdk.internal.session.room.relation.threads
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class ThreadSummariesResponse(
|
||||
@Json(name = "chunk") val chunk: List<Event>,
|
||||
@Json(name = "next_batch") val nextBatch: String?,
|
||||
@Json(name = "prev_batch") val prevBatch: String?
|
||||
)
|
|
@ -181,7 +181,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_START.stable,
|
||||
type = EventType.POLL_START.unstable,
|
||||
content = newContent.toContent().plus(additionalContent.orEmpty())
|
||||
)
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_RESPONSE.stable,
|
||||
type = EventType.POLL_RESPONSE.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
@ -226,7 +226,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_START.stable,
|
||||
type = EventType.POLL_START.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
@ -249,7 +249,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_END.stable,
|
||||
type = EventType.POLL_END.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
@ -300,7 +300,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.BEACON_LOCATION_DATA.stable,
|
||||
type = EventType.BEACON_LOCATION_DATA.unstable,
|
||||
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||
)
|
||||
|
|
|
@ -17,17 +17,23 @@
|
|||
package org.matrix.android.sdk.internal.session.room.summary
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomSummaryConstants
|
||||
import org.matrix.android.sdk.api.session.room.timeline.EventTypeFilter
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.latestEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
internal object RoomSummaryEventsHelper {
|
||||
internal class RoomSummaryEventsHelper @Inject constructor(
|
||||
matrixConfiguration: MatrixConfiguration,
|
||||
) {
|
||||
|
||||
private val previewFilters = TimelineEventFilters(
|
||||
filterTypes = true,
|
||||
allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES.map { EventTypeFilter(eventType = it, stateKey = null) },
|
||||
allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES
|
||||
.plus(matrixConfiguration.customEventTypesProvider?.customPreviewableEventTypes.orEmpty())
|
||||
.map { EventTypeFilter(eventType = it, stateKey = null) },
|
||||
filterUseless = true,
|
||||
filterRedacted = false,
|
||||
filterEdits = true
|
||||
|
|
|
@ -78,12 +78,13 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
private val crossSigningService: DefaultCrossSigningService,
|
||||
private val roomAccountDataDataSource: RoomAccountDataDataSource,
|
||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||
private val roomSummaryEventsHelper: RoomSummaryEventsHelper,
|
||||
) {
|
||||
|
||||
fun refreshLatestPreviewContent(realm: Realm, roomId: String) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId)
|
||||
if (roomSummaryEntity != null) {
|
||||
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
val latestPreviewableEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
latestPreviewableEvent?.attemptToDecrypt()
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
val encryptionEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ENCRYPTION, stateKey = "")?.root
|
||||
Timber.d("## CRYPTO: currentEncryptionEvent is $encryptionEvent")
|
||||
|
||||
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
val latestPreviewableEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
|
||||
val lastActivityFromEvent = latestPreviewableEvent?.root?.originServerTs
|
||||
if (lastActivityFromEvent != null) {
|
||||
|
@ -231,7 +232,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
fun updateSendingInformation(realm: Realm, roomId: String) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
|
||||
roomSummaryEntity.updateHasFailedSending()
|
||||
roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
roomSummaryEntity.latestPreviewableEvent = roomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,32 +16,39 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.room.threads
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.realm.Realm
|
||||
import io.realm.Sort
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.room.ResultBoundaries
|
||||
import org.matrix.android.sdk.api.session.room.threads.FetchThreadsResult
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadFilter
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadLivePageResult
|
||||
import org.matrix.android.sdk.api.session.room.threads.ThreadsService
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
import org.matrix.android.sdk.internal.database.helper.enhanceWithEditions
|
||||
import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId
|
||||
import org.matrix.android.sdk.internal.database.mapper.ThreadSummaryMapper
|
||||
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadListPageEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
|
||||
internal class DefaultThreadsService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
@UserId private val userId: String,
|
||||
private val fetchThreadTimelineTask: FetchThreadTimelineTask,
|
||||
private val fetchThreadSummariesTask: FetchThreadSummariesTask,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val timelineEventMapper: TimelineEventMapper,
|
||||
private val threadSummaryMapper: ThreadSummaryMapper
|
||||
private val threadSummaryMapper: ThreadSummaryMapper,
|
||||
private val fetchThreadSummariesTask: FetchThreadSummariesTask,
|
||||
) : ThreadsService {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -49,16 +56,58 @@ internal class DefaultThreadsService @AssistedInject constructor(
|
|||
fun create(roomId: String): DefaultThreadsService
|
||||
}
|
||||
|
||||
override fun getAllThreadSummariesLive(): LiveData<List<ThreadSummary>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{ ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId) },
|
||||
{
|
||||
threadSummaryMapper.map(it)
|
||||
override suspend fun getPagedThreadsList(userParticipating: Boolean, pagedListConfig: PagedList.Config): ThreadLivePageResult {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
realm.where<ThreadListPageEntity>().findAll().deleteAllFromRealm()
|
||||
}
|
||||
|
||||
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
|
||||
realm
|
||||
.where<ThreadSummaryEntity>().equalTo(ThreadSummaryEntityFields.PAGE.ROOM_ID, roomId)
|
||||
.sort(ThreadSummaryEntityFields.LATEST_THREAD_EVENT_ENTITY.ORIGIN_SERVER_TS, Sort.DESCENDING)
|
||||
}
|
||||
|
||||
val dataSourceFactory = realmDataSourceFactory.map {
|
||||
threadSummaryMapper.map(it)
|
||||
}
|
||||
|
||||
val boundaries = MutableLiveData(ResultBoundaries())
|
||||
|
||||
val builder = LivePagedListBuilder(dataSourceFactory, pagedListConfig).also {
|
||||
it.setBoundaryCallback(object : PagedList.BoundaryCallback<ThreadSummary>() {
|
||||
override fun onItemAtEndLoaded(itemAtEnd: ThreadSummary) {
|
||||
boundaries.postValue(boundaries.value?.copy(endLoaded = true))
|
||||
}
|
||||
|
||||
override fun onItemAtFrontLoaded(itemAtFront: ThreadSummary) {
|
||||
boundaries.postValue(boundaries.value?.copy(frontLoaded = true))
|
||||
}
|
||||
|
||||
override fun onZeroItemsLoaded() {
|
||||
boundaries.postValue(boundaries.value?.copy(zeroItemLoaded = true))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val livePagedList = monarchy.findAllPagedWithChanges(
|
||||
realmDataSourceFactory,
|
||||
builder
|
||||
)
|
||||
return ThreadLivePageResult(livePagedList, boundaries)
|
||||
}
|
||||
|
||||
override suspend fun fetchThreadList(nextBatchId: String?, limit: Int, filter: ThreadFilter): FetchThreadsResult {
|
||||
return fetchThreadSummariesTask.execute(
|
||||
FetchThreadSummariesTask.Params(
|
||||
roomId = roomId,
|
||||
from = nextBatchId,
|
||||
limit = limit,
|
||||
filter = filter
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getAllThreadSummaries(): List<ThreadSummary> {
|
||||
override suspend fun getAllThreadSummaries(): List<ThreadSummary> {
|
||||
return monarchy.fetchAllMappedSync(
|
||||
{ ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId) },
|
||||
{ threadSummaryMapper.map(it) }
|
||||
|
@ -81,12 +130,4 @@ internal class DefaultThreadsService @AssistedInject constructor(
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun fetchThreadSummaries() {
|
||||
fetchThreadSummariesTask.execute(
|
||||
FetchThreadSummariesTask.Params(
|
||||
roomId = roomId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
|||
import org.matrix.android.sdk.api.session.sync.model.SyncResponse
|
||||
import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.toDeviceTracingId
|
||||
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService
|
||||
import org.matrix.android.sdk.internal.session.sync.ProgressReporter
|
||||
import timber.log.Timber
|
||||
|
@ -48,12 +49,14 @@ internal class CryptoSyncHandler @Inject constructor(
|
|||
?.forEachIndexed { index, event ->
|
||||
progressReporter?.reportProgress(index * 100F / total)
|
||||
// Decrypt event if necessary
|
||||
Timber.tag(loggerTag.value).i("To device event from ${event.senderId} of type:${event.type}")
|
||||
Timber.tag(loggerTag.value).d("To device event msgid:${event.toDeviceTracingId()}")
|
||||
decryptToDeviceEvent(event, null)
|
||||
|
||||
if (event.getClearType() == EventType.MESSAGE &&
|
||||
event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") {
|
||||
Timber.tag(loggerTag.value).e("handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}")
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("received to-device ${event.getClearType()} from:${event.senderId} msgid:${event.toDeviceTracingId()}")
|
||||
verificationService.onToDeviceEvent(event)
|
||||
cryptoService.onToDeviceEvent(event)
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
|||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
|
||||
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.deleteOnCascade
|
||||
import org.matrix.android.sdk.internal.database.query.delete
|
||||
import org.matrix.android.sdk.internal.database.query.findAllFrom
|
||||
import org.matrix.android.sdk.internal.database.query.getDirectRooms
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
|
@ -94,7 +95,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||
|
||||
// If we get some direct chat invites, we synchronize the user account data including those.
|
||||
suspend fun synchronizeWithServerIfNeeded(invites: Map<String, InvitedRoomSync>) {
|
||||
if (invites.isNullOrEmpty()) return
|
||||
if (invites.isEmpty()) return
|
||||
val directChats = directChatsHelper.getLocalDirectMessages().toMutable()
|
||||
var hasUpdate = false
|
||||
monarchy.doWithRealm { realm ->
|
||||
|
@ -252,9 +253,17 @@ internal class UserAccountDataSyncHandler @Inject constructor(
|
|||
}
|
||||
|
||||
fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
|
||||
if (content.isNullOrEmpty()) {
|
||||
// This is a response for a deleted account data according to
|
||||
// https://github.com/ShadowJonathan/matrix-doc/blob/account-data-delete/proposals/3391-account-data-delete.md#sync
|
||||
UserAccountDataEntity.delete(realm, type)
|
||||
return
|
||||
}
|
||||
|
||||
val existing = realm.where<UserAccountDataEntity>()
|
||||
.equalTo(UserAccountDataEntityFields.TYPE, type)
|
||||
.findFirst()
|
||||
|
||||
if (existing != null) {
|
||||
// Update current value
|
||||
existing.contentStr = ContentMapper.map(content)
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntity
|
|||
import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.removeAccountData
|
||||
import org.matrix.android.sdk.internal.session.room.read.FullyReadContent
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomTagHandler
|
||||
|
@ -56,6 +57,13 @@ internal class RoomSyncAccountDataHandler @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleGeneric(roomEntity: RoomEntity, content: JsonDict?, eventType: String) {
|
||||
if (content.isNullOrEmpty()) {
|
||||
// This is a response for a deleted account data according to
|
||||
// https://github.com/ShadowJonathan/matrix-doc/blob/account-data-delete/proposals/3391-account-data-delete.md#sync
|
||||
roomEntity.removeAccountData(eventType)
|
||||
return
|
||||
}
|
||||
|
||||
val existing = roomEntity.accountData.where().equalTo(RoomAccountDataEntityFields.TYPE, eventType).findFirst()
|
||||
if (existing != null) {
|
||||
existing.contentStr = ContentMapper.map(content)
|
||||
|
|
|
@ -18,13 +18,14 @@ package org.matrix.android.sdk.internal.session.user.accountdata
|
|||
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
internal interface AccountDataAPI {
|
||||
|
||||
/**
|
||||
* Set some account_data for the client.
|
||||
* Set some account_data for the user.
|
||||
*
|
||||
* @param userId the user id
|
||||
* @param type the type
|
||||
|
@ -36,4 +37,16 @@ internal interface AccountDataAPI {
|
|||
@Path("type") type: String,
|
||||
@Body params: Any
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove an account_data for the user.
|
||||
*
|
||||
* @param userId the user id
|
||||
* @param type the type
|
||||
*/
|
||||
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc3391/user/{userId}/account_data/{type}")
|
||||
suspend fun deleteAccountData(
|
||||
@Path("userId") userId: String,
|
||||
@Path("type") type: String
|
||||
)
|
||||
}
|
||||
|
|
|
@ -42,4 +42,7 @@ internal abstract class AccountDataModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindUpdateBreadcrumbsTask(task: DefaultUpdateBreadcrumbsTask): UpdateBreadcrumbsTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindDeleteUserAccountDataTask(task: DefaultDeleteUserAccountDataTask): DeleteUserAccountDataTask
|
||||
}
|
||||
|
|
|
@ -34,10 +34,11 @@ import javax.inject.Inject
|
|||
internal class DefaultSessionAccountDataService @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val deleteUserAccountDataTask: DeleteUserAccountDataTask,
|
||||
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||
private val userAccountDataDataSource: UserAccountDataDataSource,
|
||||
private val roomAccountDataDataSource: RoomAccountDataDataSource,
|
||||
private val taskExecutor: TaskExecutor
|
||||
private val taskExecutor: TaskExecutor,
|
||||
) : SessionAccountDataService {
|
||||
|
||||
override fun getUserAccountDataEvent(type: String): UserAccountDataEvent? {
|
||||
|
@ -78,4 +79,12 @@ internal class DefaultSessionAccountDataService @Inject constructor(
|
|||
userAccountDataSyncHandler.handleGenericAccountData(realm, type, content)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUserAccountDataEventsStartWith(type: String): List<UserAccountDataEvent> {
|
||||
return userAccountDataDataSource.getAccountDataEventsStartWith(type)
|
||||
}
|
||||
|
||||
override suspend fun deleteUserAccountData(type: String) {
|
||||
deleteUserAccountDataTask.execute(DeleteUserAccountDataTask.Params(type))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.user.accountdata
|
||||
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface DeleteUserAccountDataTask : Task<DeleteUserAccountDataTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
val type: String,
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultDeleteUserAccountDataTask @Inject constructor(
|
||||
private val accountDataApi: AccountDataAPI,
|
||||
@UserId private val userId: String,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
) : DeleteUserAccountDataTask {
|
||||
|
||||
override suspend fun execute(params: DeleteUserAccountDataTask.Params) {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
accountDataApi.deleteAccountData(userId, params.type)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,6 +60,16 @@ internal class UserAccountDataDataSource @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun getAccountDataEventsStartWith(type: String): List<UserAccountDataEvent> {
|
||||
return realmSessionProvider.withRealm { realm ->
|
||||
realm
|
||||
.where(UserAccountDataEntity::class.java)
|
||||
.beginsWith(UserAccountDataEntityFields.TYPE, type)
|
||||
.findAll()
|
||||
.map(accountDataMapper::map)
|
||||
}
|
||||
}
|
||||
|
||||
private fun accountDataEventsQuery(realm: Realm, types: Set<String>): RealmQuery<UserAccountDataEntity> {
|
||||
val query = realm.where(UserAccountDataEntity::class.java)
|
||||
if (types.isNotEmpty()) {
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DevicesListResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDevicesParams
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyChangesResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UpdateDeviceInfoBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
|
||||
class DefaultSendToDeviceTaskTest {
|
||||
|
||||
private val users = listOf(
|
||||
"@alice:example.com" to listOf("D0", "D1"),
|
||||
"bob@example.com" to listOf("D2", "D3")
|
||||
)
|
||||
|
||||
private val fakeEncryptedContent = mapOf(
|
||||
"algorithm" to "m.olm.v1.curve25519-aes-sha2",
|
||||
"sender_key" to "gMObR+/4dqL5T4DisRRRYBJpn+OjzFnkyCFOktP6Eyw",
|
||||
"ciphertext" to mapOf(
|
||||
"tdwXf7006FDgzmufMCVI4rDdVPO51ecRTTT6HkRxUwE" to mapOf(
|
||||
"type" to 0,
|
||||
"body" to "AwogCA1ULEc0abGIFxMDIC9iv7ul3jqJSnapTHQ+8JJx"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private val fakeStartVerificationContent = mapOf(
|
||||
"method" to "m.sas.v1",
|
||||
"from_device" to "MNQHVEISFQ",
|
||||
"key_agreement_protocols" to listOf(
|
||||
"curve25519-hkdf-sha256",
|
||||
"curve25519"
|
||||
),
|
||||
"hashes" to listOf("sha256"),
|
||||
"message_authentication_codes" to listOf(
|
||||
"org.matrix.msc3783.hkdf-hmac-sha256",
|
||||
"hkdf-hmac-sha256",
|
||||
"hmac-sha256"
|
||||
),
|
||||
"short_authentication_string" to listOf(
|
||||
"decimal",
|
||||
"emoji"
|
||||
),
|
||||
"transaction_id" to "4wNOpkHGwGZPXjkZToooCDWfb8hsf7vW"
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `tracing id should be added to to_device contents`() {
|
||||
val fakeCryptoAPi = FakeCryptoApi()
|
||||
|
||||
val sendToDeviceTask = DefaultSendToDeviceTask(
|
||||
cryptoApi = fakeCryptoAPi,
|
||||
globalErrorReceiver = mockk(relaxed = true)
|
||||
)
|
||||
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
|
||||
users.forEach { pairOfUserDevices ->
|
||||
val userId = pairOfUserDevices.first
|
||||
pairOfUserDevices.second.forEach {
|
||||
contentMap.setObject(userId, it, fakeEncryptedContent)
|
||||
}
|
||||
}
|
||||
|
||||
val params = SendToDeviceTask.Params(
|
||||
eventType = EventType.ENCRYPTED,
|
||||
contentMap = contentMap
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(params)
|
||||
}
|
||||
|
||||
val generatedIds = mutableListOf<String>()
|
||||
users.forEach { pairOfUserDevices ->
|
||||
val userId = pairOfUserDevices.first
|
||||
pairOfUserDevices.second.forEach {
|
||||
val modifiedContent = fakeCryptoAPi.body!!.messages!![userId]!![it] as Map<*, *>
|
||||
Assert.assertNotNull("Tracing id should have been added", modifiedContent["org.matrix.msgid"])
|
||||
generatedIds.add(modifiedContent["org.matrix.msgid"] as String)
|
||||
|
||||
assertEquals(
|
||||
"The rest of the content should be the same",
|
||||
fakeEncryptedContent.keys,
|
||||
modifiedContent.toMutableMap().apply { remove("org.matrix.msgid") }.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("Id should be unique per content", generatedIds.size, generatedIds.toSet().size)
|
||||
println("modified content ${fakeCryptoAPi.body}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tracing id should not be added to verification start to_device contents`() {
|
||||
val fakeCryptoAPi = FakeCryptoApi()
|
||||
|
||||
val sendToDeviceTask = DefaultSendToDeviceTask(
|
||||
cryptoApi = fakeCryptoAPi,
|
||||
globalErrorReceiver = mockk(relaxed = true)
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject("@alice:example.com", "MNQHVEISFQ", fakeStartVerificationContent)
|
||||
|
||||
val params = SendToDeviceTask.Params(
|
||||
eventType = EventType.KEY_VERIFICATION_START,
|
||||
contentMap = contentMap
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(params)
|
||||
}
|
||||
|
||||
val modifiedContent = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *>
|
||||
Assert.assertNull("Tracing id should not have been added", modifiedContent["org.matrix.msgid"])
|
||||
|
||||
// try to force
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(
|
||||
SendToDeviceTask.Params(
|
||||
eventType = EventType.KEY_VERIFICATION_START,
|
||||
contentMap = contentMap,
|
||||
addTracingIds = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val modifiedContentForced = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *>
|
||||
Assert.assertNotNull("Tracing id should have been added", modifiedContentForced["org.matrix.msgid"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tracing id should not be added to all verification to_device contents`() {
|
||||
val fakeCryptoAPi = FakeCryptoApi()
|
||||
|
||||
val sendToDeviceTask = DefaultSendToDeviceTask(
|
||||
cryptoApi = fakeCryptoAPi,
|
||||
globalErrorReceiver = mockk(relaxed = true)
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject("@alice:example.com", "MNQHVEISFQ", emptyMap<String, Any>())
|
||||
|
||||
val verificationEvents = listOf(
|
||||
MessageType.MSGTYPE_VERIFICATION_REQUEST,
|
||||
EventType.KEY_VERIFICATION_START,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.KEY_VERIFICATION_MAC,
|
||||
EventType.KEY_VERIFICATION_CANCEL,
|
||||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_READY
|
||||
)
|
||||
|
||||
for (type in verificationEvents) {
|
||||
val params = SendToDeviceTask.Params(
|
||||
eventType = type,
|
||||
contentMap = contentMap
|
||||
)
|
||||
runBlocking {
|
||||
sendToDeviceTask.execute(params)
|
||||
}
|
||||
|
||||
val modifiedContent = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *>
|
||||
Assert.assertNull("Tracing id should not have been added", modifiedContent["org.matrix.msgid"])
|
||||
}
|
||||
}
|
||||
|
||||
internal class FakeCryptoApi : CryptoApi {
|
||||
override suspend fun getDevices(): DevicesListResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun getDeviceInfo(deviceId: String): DeviceInfo {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun uploadKeys(body: KeysUploadBody): KeysUploadResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun downloadKeysForUsers(params: KeysQueryBody): KeysQueryResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun uploadSigningKeys(params: UploadSigningKeysBody): KeysQueryResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun uploadSignatures(params: Map<String, Any>?): SignatureUploadResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun claimOneTimeKeysForUsersDevices(body: KeysClaimBody): KeysClaimResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
var body: SendToDeviceBody? = null
|
||||
override suspend fun sendToDevice(eventType: String, transactionId: String, body: SendToDeviceBody) {
|
||||
this.body = body
|
||||
}
|
||||
|
||||
override suspend fun deleteDevice(deviceId: String, params: DeleteDeviceParams) {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun deleteDevices(params: DeleteDevicesParams) {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun updateDeviceInfo(deviceId: String, params: UpdateDeviceInfoBody) {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
override suspend fun getKeyChanges(oldToken: String, newToken: String): KeyChangesResponse {
|
||||
throw java.lang.AssertionError("Should not be called")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ import org.matrix.android.sdk.test.fakes.FakeRealm
|
|||
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
||||
import org.matrix.android.sdk.test.fakes.givenFindFirst
|
||||
|
||||
class PollAggregationProcessorTest {
|
||||
class DefaultPollAggregationProcessorTest {
|
||||
|
||||
private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor()
|
||||
private val realm = FakeRealm()
|
|
@ -87,7 +87,7 @@ object PollEventsTestData {
|
|||
)
|
||||
|
||||
internal val A_POLL_START_EVENT = Event(
|
||||
type = EventType.POLL_START.stable,
|
||||
type = EventType.POLL_START.unstable,
|
||||
eventId = AN_EVENT_ID,
|
||||
originServerTs = 1652435922563,
|
||||
senderId = A_USER_ID_1,
|
||||
|
@ -96,7 +96,7 @@ object PollEventsTestData {
|
|||
)
|
||||
|
||||
internal val A_POLL_RESPONSE_EVENT = Event(
|
||||
type = EventType.POLL_RESPONSE.stable,
|
||||
type = EventType.POLL_RESPONSE.unstable,
|
||||
eventId = AN_EVENT_ID,
|
||||
originServerTs = 1652435922563,
|
||||
senderId = A_USER_ID_1,
|
||||
|
@ -105,7 +105,7 @@ object PollEventsTestData {
|
|||
)
|
||||
|
||||
internal val A_POLL_END_EVENT = Event(
|
||||
type = EventType.POLL_END.stable,
|
||||
type = EventType.POLL_END.unstable,
|
||||
eventId = AN_EVENT_ID,
|
||||
originServerTs = 1652435922563,
|
||||
senderId = A_USER_ID_1,
|
||||
|
|
|
@ -69,7 +69,7 @@ class DefaultGetActiveBeaconInfoForUserTaskTest {
|
|||
result shouldBeEqualTo currentStateEvent
|
||||
fakeStateEventDataSource.verifyGetStateEvent(
|
||||
roomId = params.roomId,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
stateKey = QueryStringValue.Equals(A_USER_ID)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ internal class DefaultStartLiveLocationShareTaskTest {
|
|||
val expectedParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = A_USER_ID,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
body = expectedBeaconContent
|
||||
)
|
||||
fakeSendStateTask.verifyExecuteRetry(
|
||||
|
|
|
@ -79,7 +79,7 @@ class DefaultStopLiveLocationShareTaskTest {
|
|||
val expectedSendParams = SendStateTask.Params(
|
||||
roomId = params.roomId,
|
||||
stateKey = A_USER_ID,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
|
||||
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
|
||||
body = expectedBeaconContent
|
||||
)
|
||||
fakeSendStateTask.verifyExecuteRetry(
|
||||
|
|
|
@ -79,7 +79,7 @@ class LiveLocationShareRedactionEventProcessorTest {
|
|||
@Test
|
||||
fun `given a redacted live location share event when processing it then related summaries are deleted from database`() = runTest {
|
||||
val event = Event(eventId = AN_EVENT_ID, redacts = A_REDACTED_EVENT_ID)
|
||||
val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.stable)
|
||||
val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.unstable)
|
||||
fakeRealm.givenWhere<EventEntity>()
|
||||
.givenEqualTo(EventEntityFields.EVENT_ID, A_REDACTED_EVENT_ID)
|
||||
.givenFindFirst(redactedEventEntity)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue