mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
Merge branch 'develop' into feature/fga/timeline_chunks_rework
This commit is contained in:
commit
3d31ba963d
257 changed files with 6133 additions and 2416 deletions
9
.github/workflows/sanity_test.yml
vendored
9
.github/workflows/sanity_test.yml
vendored
|
@ -56,10 +56,10 @@ jobs:
|
|||
java-version: '11'
|
||||
- name: Run sanity tests on API ${{ matrix.api-level }}
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
continue-on-error: true # allow pipeline to upload failure results
|
||||
with:
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
api-level: ${{ matrix.api-level }}
|
||||
profile: 24 # Pixel 5
|
||||
emulator-build: 7425822 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||
script: |
|
||||
adb root
|
||||
|
@ -67,13 +67,12 @@ jobs:
|
|||
touch emulator.log
|
||||
chmod 777 emulator.log
|
||||
adb logcat >> emulator.log &
|
||||
./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest || adb pull storage/emulated/0/Pictures/failure_screenshots
|
||||
|
||||
./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest || adb pull storage/emulated/0/Pictures/failure_screenshots && exit 1
|
||||
- name: Upload Failing Test Report Log
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: sanity-error-results
|
||||
path: |
|
||||
emulator.log
|
||||
failure_screenshots/
|
||||
failure_screenshots/
|
|
@ -7,6 +7,8 @@ on:
|
|||
jobs:
|
||||
sync-emojis:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip in forks
|
||||
if: github.repository == 'vector-im/element-android'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
|
@ -39,6 +41,8 @@ jobs:
|
|||
|
||||
sync-sas-strings:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip in forks
|
||||
if: github.repository == 'vector-im/element-android'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
|
|
23
.github/workflows/triage-move-labelled.yml
vendored
23
.github/workflows/triage-move-labelled.yml
vendored
|
@ -99,3 +99,26 @@ jobs:
|
|||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc2KCw"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_threads_issues:
|
||||
name: Move A-Threads to Thread board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Threads')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:String!,$contentid:String!) {
|
||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc0rRA"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
32
CHANGES.md
32
CHANGES.md
|
@ -1,3 +1,35 @@
|
|||
Changes in Element v1.3.8 (2021-11-17)
|
||||
======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Android 12 support ([#4433](https://github.com/vector-im/element-android/issues/4433))
|
||||
- Make notification text spoiler aware ([#3477](https://github.com/vector-im/element-android/issues/3477))
|
||||
- Poll Feature - Create Poll Screen (Disabled for now) ([#4367](https://github.com/vector-im/element-android/issues/4367))
|
||||
- Adds support for images inside message notifications ([#4402](https://github.com/vector-im/element-android/issues/4402))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Render markdown in room list ([#452](https://github.com/vector-im/element-android/issues/452))
|
||||
- Fix incorrect cropping of conversation icons ([#4424](https://github.com/vector-im/element-android/issues/4424))
|
||||
- Fix potential NullPointerException crashes in Room and User account data sources ([#4428](https://github.com/vector-im/element-android/issues/4428))
|
||||
- Unable to establish Olm outbound session from fallback key ([#4446](https://github.com/vector-im/element-android/issues/4446))
|
||||
- Fixes intermittent crash on sign out due to the session being incorrectly recreated whilst being closed ([#4480](https://github.com/vector-im/element-android/issues/4480))
|
||||
|
||||
SDK API changes ⚠️
|
||||
------------------
|
||||
- Add content scanner API from MSC1453
|
||||
API documentation : https://github.com/matrix-org/matrix-content-scanner#api ([#4392](https://github.com/vector-im/element-android/issues/4392))
|
||||
- Breaking SDK API change to PushRuleListener, the separated callbacks have been merged into one with a data class which includes all the previously separated push information ([#4401](https://github.com/vector-im/element-android/issues/4401))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- Finish migration from RxJava to Flow ([#4219](https://github.com/vector-im/element-android/issues/4219))
|
||||
- Remove redundant text in feature request issue form ([#4257](https://github.com/vector-im/element-android/issues/4257))
|
||||
- Add and improve issue triage workflows ([#4435](https://github.com/vector-im/element-android/issues/4435))
|
||||
- Update issue template to bring in line with element-web ([#4452](https://github.com/vector-im/element-android/issues/4452))
|
||||
|
||||
|
||||
Changes in Element v1.3.7 (2021-11-04)
|
||||
======================================
|
||||
|
||||
|
|
14
README.md
14
README.md
|
@ -27,6 +27,20 @@ At each Element release, the SDK module is copied to a dedicated repository: htt
|
|||
The version 1.0.0 of Element still misses some features which was previously included in Riot-Android.
|
||||
The team will work to add them on a regular basis.
|
||||
|
||||
# Releases to app stores
|
||||
|
||||
There is some delay between when a release is created and when it appears in the app stores (Google Play Store and F-Droid). Here are some of the reasons:
|
||||
|
||||
* Not all versioned releases that appear on GitHub are considered stable. Each release is first considered beta: this continues for at least two days. If the release is stable (no serious issues or crashes are reported), then it is released as a production release in Google Play Store, and a request is sent to F-Droid too.
|
||||
* Each release on the Google Play Store undergoes review by Google before it comes out. This can take an unpredictable amount of time. In some cases it has taken several weeks.
|
||||
* In order for F-Droid to guarantee that the app you receive exactly matches the public source code, they build releases themselves. When a release is considered stable, Element staff inform the F-Droid maintainers and it is added to the build queue. Depending on the load on F-Droid's infrastructure, it can take some time for releases to be built. This always takes at least 24 hours, and can take several days.
|
||||
|
||||
If you would like to receive releases more quickly (bearing in mind that they may not be stable) you have a number of options:
|
||||
|
||||
1. [Sign up to receive beta releases](https://play.google.com/apps/testing/im.vector.app) via the Google Play Store.
|
||||
2. Install a [release APK](https://github.com/vector-im/element-android/releases) directly - download the relevant .apk file and allow installing from untrusted sources in your device settings. Note: these releases are the Google Play version, which depend on some Google services. If you prefer to avoid that, try the latest dev builds, and choose the F-Droid version.
|
||||
3. If you're really brave, install the [very latest dev build](https://buildkite.com/matrix-dot-org/element-android/builds/latest?branch=develop&state=passed) - click on *Assemble (GPlay or FDroid) Debug version* then on *Artifacts*.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please refer to [CONTRIBUTING.md](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md) if you want to contribute on Matrix Android projects!
|
||||
|
|
|
@ -69,9 +69,9 @@ allprojects {
|
|||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||
// Jitsi repo
|
||||
maven {
|
||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.1.0"
|
||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.10.0"
|
||||
// Note: to test Jitsi release you can use a local file like this:
|
||||
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.1.0"
|
||||
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.10.0"
|
||||
}
|
||||
google()
|
||||
mavenCentral()
|
||||
|
|
1
changelog.d/3449.bugfix
Normal file
1
changelog.d/3449.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fixes left over text when inserting emojis via the ':' menu and replaces the last typed ':' rather than the one at the end of the message
|
1
changelog.d/3833.bugfix
Normal file
1
changelog.d/3833.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fixing queued voice message failing to send or retry
|
1
changelog.d/4022.bugfix
Normal file
1
changelog.d/4022.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Keeping device screen on whilst recording and playing back voice messages
|
1
changelog.d/4067.bugfix
Normal file
1
changelog.d/4067.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Allow voice messages to continue recording during device rotation
|
1
changelog.d/4144.bugfix
Normal file
1
changelog.d/4144.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Allowing users to hang up VOIP calls during the initialisation phase (avoids getting stuck in the call screen if something goes wrong)
|
|
@ -1 +0,0 @@
|
|||
Finish migration from RxJava to Flow
|
1
changelog.d/4246.feature
Normal file
1
changelog.d/4246.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Make Element Android Thread aware
|
|
@ -1 +0,0 @@
|
|||
Remove redundant text in feature request issue form
|
1
changelog.d/4338.bugfix
Normal file
1
changelog.d/4338.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Make the verification shields the same in Element Web and Element Android
|
1
changelog.d/4343.bugfix
Normal file
1
changelog.d/4343.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fix a display issue in the composer when the replied message is changed.
|
|
@ -1 +0,0 @@
|
|||
Poll Feature - Create Poll Screen (Disabled for now)
|
|
@ -1 +0,0 @@
|
|||
Breaking SDK API change to PushRuleListener, the separated callbacks have been merged into one with a data class which includes all the previously separated push information
|
|
@ -1 +0,0 @@
|
|||
Adds support for images inside message notifications
|
|
@ -1 +0,0 @@
|
|||
Fix incorrect cropping of conversation icons
|
|
@ -1 +0,0 @@
|
|||
Fix potential NullPointerException crashes in Room and User account data sources
|
|
@ -1 +0,0 @@
|
|||
Add and improve issue triage workflows
|
|
@ -1 +0,0 @@
|
|||
Unable to establish Olm outbound session from fallback key
|
|
@ -1 +0,0 @@
|
|||
Update issue template to bring in line with element-web
|
1
changelog.d/4488.bugfix
Normal file
1
changelog.d/4488.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Dismissing the Fdroid variant Listening for notifications on sign out, fixes crash when tapping the notification when signed out
|
1
changelog.d/4504.misc
Normal file
1
changelog.d/4504.misc
Normal file
|
@ -0,0 +1 @@
|
|||
Upgrade Jitsi lib (and so webrtc) from Jitsi android-sdk-3.1.0 to android-sdk-3.10.0
|
1
changelog.d/4507.misc
Normal file
1
changelog.d/4507.misc
Normal file
|
@ -0,0 +1 @@
|
|||
Improve crypto logs to help debug decryption failures
|
1
changelog.d/4515.misc
Normal file
1
changelog.d/4515.misc
Normal file
|
@ -0,0 +1 @@
|
|||
Voice recording mic button refactor with small animation tweaks in preparation for voice drafts
|
1
changelog.d/4520.bugfix
Normal file
1
changelog.d/4520.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fix a crash when displaying the bootstrap bottom sheet
|
1
changelog.d/4539.bugfix
Normal file
1
changelog.d/4539.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Remove duplicated settings declaration
|
1
changelog.d/4552.bugfix
Normal file
1
changelog.d/4552.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fixes .ogg files failing to upload to rooms
|
|
@ -11,7 +11,7 @@ def gradle = "7.0.3"
|
|||
// Ref: https://kotlinlang.org/releases.html
|
||||
def kotlin = "1.5.31"
|
||||
def kotlinCoroutines = "1.5.2"
|
||||
def dagger = "2.40.1"
|
||||
def dagger = "2.40.3"
|
||||
def retrofit = "2.9.0"
|
||||
def arrow = "0.8.2"
|
||||
def markwon = "4.6.2"
|
||||
|
@ -26,7 +26,7 @@ def jjwt = "0.11.2"
|
|||
def vanniktechEmoji = "0.8.0"
|
||||
|
||||
// Testing
|
||||
def mockk = "1.12.0"
|
||||
def mockk = "1.12.1"
|
||||
def espresso = "3.4.0"
|
||||
def androidxTest = "1.4.0"
|
||||
|
||||
|
@ -45,13 +45,13 @@ ext.libs = [
|
|||
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
|
||||
],
|
||||
androidx : [
|
||||
'appCompat' : "androidx.appcompat:appcompat:1.3.1",
|
||||
'appCompat' : "androidx.appcompat:appcompat:1.4.0",
|
||||
'core' : "androidx.core:core-ktx:1.7.0",
|
||||
'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1",
|
||||
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3",
|
||||
'fragmentKtx' : "androidx.fragment:fragment-ktx:1.3.6",
|
||||
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.1",
|
||||
'work' : "androidx.work:work-runtime-ktx:2.7.0",
|
||||
'fragmentKtx' : "androidx.fragment:fragment-ktx:1.4.0",
|
||||
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.2",
|
||||
'work' : "androidx.work:work-runtime-ktx:2.7.1",
|
||||
'autoFill' : "androidx.autofill:autofill:1.1.0",
|
||||
'preferenceKtx' : "androidx.preference:preference-ktx:1.1.1",
|
||||
'junit' : "androidx.test.ext:junit:1.1.3",
|
||||
|
|
|
@ -18,7 +18,7 @@ The generated maven repository is then host in the project https://github.com/ve
|
|||
|
||||
Update the script `./tools/jitsi/build_jisti_libs.sh` with the tag of the project `https://github.com/jitsi/jitsi-meet`.
|
||||
|
||||
Currently we are building the version with the tag `android-sdk-3.1.0`.
|
||||
Currently we are building the version with the tag `android-sdk-3.10.0`.
|
||||
|
||||
### Run the build script
|
||||
|
||||
|
@ -35,7 +35,7 @@ It will build the Jitsi Meet Android library and put every generated files in th
|
|||
- Update the file `./build.gradle` to use the previously created local Maven repository. Currently we have this line:
|
||||
|
||||
```groovy
|
||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/master/android-sdk-3.1.0"
|
||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/master/android-sdk-3.10.0"
|
||||
```
|
||||
|
||||
You can uncomment and update the line starting with `// url "file://...` and comment the line starting with `url`, to test the library using the locally generated Maven repository.
|
||||
|
@ -43,13 +43,13 @@ You can uncomment and update the line starting with `// url "file://...` and com
|
|||
- Update the dependency of the Jitsi Meet library in the file `./vector/build.gradle`. Currently we have this line:
|
||||
|
||||
```groovy
|
||||
implementation('org.jitsi.react:jitsi-meet-sdk:3.1.0')
|
||||
implementation('org.jitsi.react:jitsi-meet-sdk:3.10.0')
|
||||
```
|
||||
|
||||
- Update the dependency of the WebRTC library in the file `./vector/build.gradle`. Currently we have this line:
|
||||
|
||||
```groovy
|
||||
implementation('com.facebook.react:react-native-webrtc:1.87.3-jitsi-6624067@aar')
|
||||
implementation('com.facebook.react:react-native-webrtc:1.92.1-jitsi-9093212@aar')
|
||||
```
|
||||
|
||||
- Perform a gradle sync and build the project
|
||||
|
@ -74,7 +74,7 @@ If all the tests are passed, you can export the generated Jitsi library to our M
|
|||
- Update the file `./build.gradle` to use the previously created Maven repository. Currently we have this line:
|
||||
|
||||
```groovy
|
||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/master/android-sdk-3.1.0"
|
||||
url "https://github.com/vector-im/jitsi_libre_maven/raw/master/android-sdk-3.10.0"
|
||||
```
|
||||
|
||||
- Build the project and perform the sanity tests again.
|
||||
|
|
2
fastlane/metadata/android/cs-CZ/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Přidání podpory přítomnosti pro místnost s přímými zprávami (poznámka: přítomnost je na matrix.org zakázána). Opět přidána podpora Android Auto.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/cs-CZ/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: Přidání podpory přítomnosti pro místnost s přímými zprávami (poznámka: přítomnost je na matrix.org zakázána). Opět přidána podpora Android Auto.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1,2 +1,2 @@
|
|||
Main changes in this version: Bug fixes mainly regarding the notifications.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.7
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
|
2
fastlane/metadata/android/en-US/changelogs/40103080.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40103080.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Bug fixes!
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.8
|
2
fastlane/metadata/android/et/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: Lisasime otsevestlustele kasutaja võrguolekute toe (matrix.org puhul on välja lülitatud) ja uuesti lisasime Android Auto toe.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/et/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: Lisasime otsevestlustele kasutaja võrguolekute toe (matrix.org puhul on välja lülitatud) ja uuesti lisasime Android Auto toe.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
2
fastlane/metadata/android/fr-FR/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : ajout du support pour les indicateurs de présence, dans les conversations privées (attention : les indicateurs de présence sont désactivés sur matrix.org). Réactivation de la prise en charge de Android Auto.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/fr-FR/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : ajout du support pour les indicateurs de présence, dans les conversations privées (attention : les indicateurs de présence sont désactivés sur matrix.org). Réactivation de la prise en charge de Android Auto.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1 +1 @@
|
|||
Groepsberjochtetsjinst - fersifere berjochten, groeps petearen en fideo skilje
|
||||
Groepsberjochtetsjinst - fersifere berjochten, groepspetearen en fideobelje
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element - Feilige Berjochtetsjinst
|
||||
Element - Feilige berjochtetsjinst
|
||||
|
|
2
fastlane/metadata/android/hu-HU/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: Állapot állítási lehetőség közvetlen beszélgetéseknél (megj.: a matrix.org-on az állapot jelzés ki van kapcsolva). Újra elérhető az Android Auto.
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/hu-HU/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: Állapot állítási lehetőség közvetlen beszélgetéseknél (megj.: a matrix.org-on az állapot jelzés ki van kapcsolva). Újra elérhető az Android Auto.
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1,2 +1,2 @@
|
|||
Perubahan utama di versi ini: Menambahkan dukungan presensi, untuk ruangan Pesan Langsung (diingat bahwa presensi dinonaktifkan di matrix.org). Menambahkan lagi dukungan Android Auto.
|
||||
Perubahan utama di versi ini: Tambahkan dukungan presensi, untuk ruangan Pesan Langsung (diingat bahwa presensi dinonaktifkan di matrix.org). Tambahkan lagi dukungan Android Auto.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.4
|
||||
|
|
2
fastlane/metadata/android/id/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Tambahkan dukungan presensi, untuk ruangan Pesan Langsung (diingat bahwa presensi dinonaktifkan di matrix.org). Tambahkan lagi dukungan Android Auto.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/id/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Tambahkan dukungan presensi, untuk ruangan Pesan Langsung (catatan: presensi dinonaktifkan di matrix.org). Tambahkan lagi dukungan Android Auto.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1,42 +1,42 @@
|
|||
Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi obrolan ini menggunakan enkripsi ujung-ke-ujung untuk memberikan konferensi video, berbagi file, dan panggilan suara.
|
||||
Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi perpesanan ini menggunakan enkripsi ujung-ke-ujung untuk memberikan konferensi video, pembagian file, dan panggilan suara yang aman.
|
||||
|
||||
<b>Fitur Element termasuk:</b>
|
||||
<b>Fitur Element termasuk</b>
|
||||
- Alat komunikasi online yang canggih
|
||||
- Pesan terenkripsi sepenuhnya untuk memungkinkan komunikasi perusahaan yang lebih aman, bahkan untuk pekerja jarak jauh
|
||||
- Obrolan terdesentralisasi berdasarkan framework sumber-terbuka Matrix
|
||||
- Berbagi file dengan aman dengan data terenkripsi saat mengelola proyek
|
||||
- Obrolan video dengan VoIP dan berbagi layar
|
||||
- Pesan-pesan yang dienkripsi sepenuhnya untuk memungkinkan komunikasi perusahaan yang lebih aman, bahkan untuk pekerja jarak jauh
|
||||
- Obrolan terdesentralisasi berdasarkan kerangka Matrix yang sumber terbuka
|
||||
- Pembagian file aman dengan data terenkripsi saat mengelola proyek
|
||||
- Obrolan video dengan VoIP dan pembagian layar
|
||||
- Integrasi yang mudah dengan alat kolaborasi online favorit Anda, alat manajemen proyek, layanan VoIP dan aplikasi perpesanan tim lainnya
|
||||
|
||||
Element benar-benar berbeda dari aplikasi perpesanan dan kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi. Matrix memungkinkan hosting sendiri untuk memberi pengguna kepemilikan maksimum dan kontrol data dan pesan mereka.
|
||||
Element benar-benar berbeda dari aplikasi perpesanan dan aplikasi kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi.
|
||||
|
||||
<b>Pesan privasi dan terenkripsi</b>
|
||||
Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang ditandatangani secara silang.
|
||||
<b>Perpesanan dengan privasi dan enkripsi</b>
|
||||
Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu dengan enkripsi ujung-ke-ujung dan verifikasi perangkat menggunakan penandatanganan silang.
|
||||
|
||||
Element memberi Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan aman dengan siapa pun di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan dengan aplikasi seperti Slack.
|
||||
Element memberikan Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan siapa saja secara aman di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan aplikasi-aplikasi seperti Slack.
|
||||
|
||||
<b>Element dapat dihost sendiri</b>
|
||||
Untuk memungkinkan lebih banyak kendali atas data dan percakapan sensitif Anda, Element bisa dihost sendiri atau Anda dapat memilih host berbasis Matrix - standar untuk komunikasi terdesentralisasi sumber-terbuka. Element memberi Anda privasi, kepatuhan keamanan, dan fleksibilitas integrasi.
|
||||
Untuk memungkinkan lebih banyak kendali atas data dan pesan-pesan sensitif Anda, Element dapat dihost sendiri atau Anda dapat memilih host berbasis Matrix, standar untuk komunikasi terdesentralisasi sumber terbuka. Element memberi Anda privasi, kepatuhan keamanan, dan fleksibilitas integrasi.
|
||||
|
||||
<b>Miliki data Anda</b>
|
||||
Anda memutuskan di mana menyimpan data dan pesan Anda. Tanpa risiko penambangan data atau akses dari pihak ketiga.
|
||||
Anda memutuskan di mana untuk menyimpan data dan pesan-pesan Anda, tanpa risiko penambangan data atau akses dari pihak ketiga.
|
||||
|
||||
Element menempatkan Anda dalam kendali dengan cara yang berbeda:
|
||||
1. Dapatkan akun gratis pada server publik matrix.org yang dihost oleh pengembang Matrix, atau memilih dari ribuan server publik yang dihost oleh sukarelawan
|
||||
2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri
|
||||
3. Daftar untuk akun di server khusus dengan hanya berlangganan platform hosting Element Matrix Services
|
||||
3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element
|
||||
|
||||
<b>Pesan terbuka dan kolaborasi</b>
|
||||
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, apakah mereka menggunakan Element, aplikasi Matrix lain atau bahkan jika mereka menggunakan aplikasi perpesanan yang berbeda.
|
||||
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda.
|
||||
|
||||
<b>Sangat aman</b>
|
||||
Enkripsi ujung-ke-ujung yang nyata (hanya mereka yang dalam percakapan dapat mendekripsi pesan), dan verifikasi perangkat menggunakan penandatanganan-silang.
|
||||
Enkripsi ujung-ke-ujung yang nyata (hanya mereka yang dalam obrolan dapat mendekripsi pesan), dan verifikasi perangkat menggunakan penandatanganan silang.
|
||||
|
||||
<b>Komunikasi dan integrasi lengkap</b>
|
||||
Perpesanan, panggilan suara dan video, berbagi file, berbagi layar dan banyak integrasi, bot dan widget. Buat ruangan, komunitas, tetap terhubung dan selesaikan hal-hal.
|
||||
Perpesanan, panggilan suara dan video, pembagian file, pembagian layar dan banyak integrasi bot dan widget. Buat ruangan dan komunitas, tetap terhubung dan selesaikan hal-hal penting.
|
||||
|
||||
<b>Ambil di mana Anda tinggalkan</b>
|
||||
Tetap terhubung di mana pun Anda berada dengan riwayat pesan yang sepenuhnya disinkronkan di semua perangkat Anda dan di web di https://app.element.io
|
||||
Tetap terhubung di mana Anda berada, dengan riwayat pesan yang disinkronkan di semua perangkat Anda dan web di https://app.element.io
|
||||
|
||||
<b>Open source</b>
|
||||
Element Android adalah proyek sumber terbuka, di-host oleh GitHub. Silakan melaporkan bug dan/atau membuat kontribusi ke pengembangannya di https://github.com/vector-im/element-android
|
||||
<b>Sumber terbuka</b>
|
||||
Element Android adalah proyek sumber terbuka, dihost oleh GitHub. Silakan laporkan masalah yang Anda temukan, atau membuat kontribusi ke pengembangannya di https://github.com/vector-im/element-android
|
||||
|
|
|
@ -1 +1 @@
|
|||
Perpesanan grup - pesan terenkripsi, panggilan grup dan video
|
||||
Perpesanan grup - perpesanan, panggilan suara dan video grup terenkripsi
|
||||
|
|
2
fastlane/metadata/android/it-IT/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: aggiunto supporto alla presenza per messaggi diretti (nota: la presenza è disattivata su matrix.org). Aggiunto di nuovo il supporto ad Android Auto.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/it-IT/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: aggiunto supporto alla presenza per i messaggi diretti (nota: la presenza è disattivata su matrix.org). Aggiunto di nuovo il supporto a Android Auto.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
2
fastlane/metadata/android/pt-BR/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: Adicionar suporte a Presença, para sala de Mensagem Direta (nota: presença está desabilitada em matrix.org). Adicionar de novo suporte a Android Auto.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/pt-BR/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: Adicionar suporte a Presença, para sala de Mensagem Direta (nota: presença está desabilitada em matrix.org). Adicionar de novo suporte a Android Auto.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1,2 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: Shtim mbulimi për Prani, për dhomë Mesazh i Drejtpërdrejtë (shënim: në matrix.org prania është e çaktivizuar. Shtim sërish i mbulimit për Android Auto.
|
||||
Ndryshimet kryesore në këtë version: Shtim mbulimi për Prani, për dhomë Mesazh i Drejtpërdrejtë (shënim: në matrix.org prania është e çaktivizuar). Shtim sërish i mbulimit për Android Auto.
|
||||
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.4
|
||||
|
|
2
fastlane/metadata/android/sq/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/sq/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: Shtim mbulimi për Prani, për dhomën Mesazh i Drejtpërdrejtë (shënim: prania është e çaktivizuar në matrix.org). Shtim sërish i mbulimit për Android Auto.
|
||||
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/sq/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/sq/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Ndryshimet kryesore në këtë version: Shtim mbulimi për Prani, për dhomën Mesazh i Drejtpërdrejtë (shënim: prania është e çaktivizuar në matrix.org). Shtim sërish i mbulimit për Android Auto.
|
||||
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
2
fastlane/metadata/android/sv-SE/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/sv-SE/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: Lägg till närvarostöd för direktmeddelanden (obs: närvaro är inaktiverat på matrix.org). Lägg till stöd för Android Auto igen.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/sv-SE/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/sv-SE/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Huvudsakliga ändringar i den här versionen: Lägg till närvarostöd för direktmeddelanden (obs: närvaro är inaktiverat på matrix.org). Lägg till stöd för Android Auto igen.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1,2 +1,2 @@
|
|||
Основні зміни в цій версії: Додано підтримку присутності для кімнати особистих повідомлень (примітка: присутність вимкнено на matrix.org. Знову додано підтримку Android Auto.
|
||||
Основні зміни в цій версії: Додано підтримку присутності для кімнати особистих повідомлень (примітка: присутність вимкнено на matrix.org). Знову додано підтримку Android Auto.
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.4
|
||||
|
|
2
fastlane/metadata/android/uk/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: Додано підтримку присутності для кімнати особистих повідомлень (примітка: присутність вимкнена на matrix.org). Знову додано підтримку Android Auto.
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/uk/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: Додано підтримку присутності для кімнати особистих повідомлень (примітка: присутність вимкнена на matrix.org). Знову додано підтримку Android Auto.
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
|
@ -1,2 +1,2 @@
|
|||
此版本主要变化:为 Direct Message 聊天室添加 Presence 支持 (注意:Presence 在matrix.org 上是禁用的。再次添加 Android Auto 支持。
|
||||
此版本主要变化:为 Direct Message 聊天室添加 Presence 支持 (注意:presence 在 matrix.org 上是禁用的)。再次添加 Android Auto 支持。
|
||||
完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.4
|
||||
|
|
2
fastlane/metadata/android/zh-CN/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/zh-CN/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本的主要变化:为私信聊天室添加 Presence 支持 (注意:在 matrix.org 上 Presence 是禁用的)。再次添加 Android Auto 支持。
|
||||
完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/zh-CN/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/zh-CN/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本的主要变化:为私信聊天室添加 Presence 支持(注意:在 matrix.org 上 Presence 是禁用的)。再次添加 Android Auto 支持。
|
||||
完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
2
fastlane/metadata/android/zh-TW/changelogs/40103050.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40103050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:為直接訊息聊天室新增 Presence 支援(請注意:此功能在 matrix.org 上停用)。加回 Android Auto 支援。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.5
|
2
fastlane/metadata/android/zh-TW/changelogs/40103060.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40103060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:為直接訊息聊天室新增 Presence 支援(請注意:此功能在 matrix.org 上停用)。加回 Android Auto 支援。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.6
|
113
library/ui-styles/src/debug/res/layout/debug_social_login.xml
Normal file
113
library/ui-styles/src/debug/res/layout/debug_social_login.xml
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#DDD"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Light" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/element_background_light"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Google.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Facebook.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Github.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Gitlab.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Apple.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Twitter.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Dark" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/element_background_dark"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Google.Dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Facebook.Dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Github.Dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Gitlab.Dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Apple.Dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Vector.Button.Outlined.SocialLogin.Twitter.Dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Continue with XXX" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -9,7 +9,7 @@
|
|||
<item name="iconGravity">start</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textAlignment">center</item>
|
||||
<item name="android:paddingStart">2dp</item>
|
||||
<item name="android:paddingStart">4dp</item>
|
||||
<!-- Compensate icon size to center text correctly-->
|
||||
<item name="android:paddingEnd">38dp</item>
|
||||
<item name="android:clipToPadding">false</item>
|
||||
|
|
|
@ -129,7 +129,9 @@
|
|||
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
|
||||
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
|
||||
|
||||
<item name="vctr_social_login_button_google_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Google.Dark</item>
|
||||
<!-- For Google button style, use same values than for light theme, for a better rendering (white background)
|
||||
see https://github.com/vector-im/element-android/issues/4285#issuecomment-974270998 -->
|
||||
<item name="vctr_social_login_button_google_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Google.Light</item>
|
||||
<item name="vctr_social_login_button_github_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Github.Dark</item>
|
||||
<item name="vctr_social_login_button_facebook_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Facebook.Dark</item>
|
||||
<item name="vctr_social_login_button_twitter_style">@style/Widget.Vector.Button.Outlined.SocialLogin.Twitter.Dark</item>
|
||||
|
|
|
@ -31,7 +31,7 @@ android {
|
|||
// that the app's state is completely cleared between tests.
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.3.8\""
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.3.9\""
|
||||
|
||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
|
||||
|
@ -115,7 +115,7 @@ dependencies {
|
|||
implementation libs.squareup.retrofit
|
||||
implementation libs.squareup.retrofitMoshi
|
||||
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.2"))
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
|
||||
implementation 'com.squareup.okhttp3:okhttp'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor'
|
||||
implementation 'com.squareup.okhttp3:okhttp-urlconnection'
|
||||
|
@ -158,10 +158,10 @@ dependencies {
|
|||
implementation libs.apache.commonsImaging
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.37'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.38'
|
||||
|
||||
testImplementation libs.tests.junit
|
||||
testImplementation 'org.robolectric:robolectric:4.7'
|
||||
testImplementation 'org.robolectric:robolectric:4.7.2'
|
||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||
testImplementation libs.mockk.mockk
|
||||
|
|
|
@ -20,7 +20,18 @@ package org.matrix.android.sdk.api
|
|||
* This class contains pattern to match Matrix Url, aka mxc urls
|
||||
*/
|
||||
object MatrixUrls {
|
||||
/**
|
||||
* "mxc" scheme, including "://". So "mxc://"
|
||||
*/
|
||||
const val MATRIX_CONTENT_URI_SCHEME = "mxc://"
|
||||
|
||||
/**
|
||||
* Return true if the String starts with "mxc://"
|
||||
*/
|
||||
fun String.isMxcUrl() = startsWith(MATRIX_CONTENT_URI_SCHEME)
|
||||
|
||||
/**
|
||||
* Remove the "mxc://" prefix. No op if the String is not a Mxc URL
|
||||
*/
|
||||
fun String.removeMxcPrefix() = removePrefix(MATRIX_CONTENT_URI_SCHEME)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.matrix.android.sdk.api.failure
|
|||
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.contentscanner.ContentScannerError
|
||||
import org.matrix.android.sdk.api.session.contentscanner.ScanFailure
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import java.io.IOException
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
@ -100,3 +102,19 @@ fun Throwable.isRegistrationAvailabilityError(): Boolean {
|
|||
error.code == MatrixError.M_INVALID_USERNAME ||
|
||||
error.code == MatrixError.M_EXCLUSIVE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert to a ScanFailure. Return null in the cases it's not possible
|
||||
*/
|
||||
fun Throwable.toScanFailure(): ScanFailure? {
|
||||
return if (this is Failure.OtherServerError) {
|
||||
tryOrNull {
|
||||
MoshiProvider.providesMoshi()
|
||||
.adapter(ContentScannerError::class.java)
|
||||
.fromJson(errorBody)
|
||||
}
|
||||
?.let { ScanFailure(it, httpCode, this) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ open class LoggerTag(_value: String, parentTag: LoggerTag? = null) {
|
|||
|
||||
object SYNC : LoggerTag("SYNC")
|
||||
object VOIP : LoggerTag("VOIP")
|
||||
object CRYPTO : LoggerTag("CRYPTO")
|
||||
|
||||
val value: String = if (parentTag == null) {
|
||||
_value
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.matrix.android.sdk.api.session.cache.CacheService
|
|||
import org.matrix.android.sdk.api.session.call.CallSignalingService
|
||||
import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker
|
||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||
import org.matrix.android.sdk.api.session.contentscanner.ContentScannerService
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.events.EventService
|
||||
import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
|
||||
|
@ -192,6 +193,11 @@ interface Session :
|
|||
*/
|
||||
fun cryptoService(): CryptoService
|
||||
|
||||
/**
|
||||
* Returns the ContentScannerService associated with the session
|
||||
*/
|
||||
fun contentScannerService(): ContentScannerService
|
||||
|
||||
/**
|
||||
* Returns the identity service associated with the session
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,8 @@ data class ContentAttachmentData(
|
|||
FILE,
|
||||
IMAGE,
|
||||
AUDIO,
|
||||
VIDEO
|
||||
VIDEO,
|
||||
VOICE_MESSAGE
|
||||
}
|
||||
|
||||
fun getSafeMimeType() = mimeType?.normalizeMimeType()
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.content
|
||||
|
||||
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
|
||||
|
||||
/**
|
||||
* This interface defines methods for accessing content from the current session.
|
||||
*/
|
||||
|
@ -39,6 +41,15 @@ interface ContentUrlResolver {
|
|||
*/
|
||||
fun resolveFullSize(contentUrl: String?): String?
|
||||
|
||||
/**
|
||||
* Get the ResolvedMethod to download a URL
|
||||
*
|
||||
* @param contentUrl the Matrix media content URI (in the form of "mxc://...").
|
||||
* @param elementToDecrypt Encryption data may be required if you use a content scanner
|
||||
* @return the Method to access resource, or null if invalid
|
||||
*/
|
||||
fun resolveForDownload(contentUrl: String?, elementToDecrypt: ElementToDecrypt? = null): ResolvedMethod?
|
||||
|
||||
/**
|
||||
* Get the actual URL for accessing the thumbnail image of a given Matrix media content URI.
|
||||
*
|
||||
|
@ -49,4 +60,9 @@ interface ContentUrlResolver {
|
|||
* @return the URL to access the described resource, or null if the url is invalid.
|
||||
*/
|
||||
fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String?
|
||||
|
||||
sealed class ResolvedMethod {
|
||||
data class GET(val url: String) : ResolvedMethod()
|
||||
data class POST(val url: String, val jsonBody: String) : ResolvedMethod()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.contentscanner
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ContentScannerError(
|
||||
@Json(name = "info") val info: String? = null,
|
||||
@Json(name = "reason") val reason: String? = null
|
||||
) {
|
||||
companion object {
|
||||
// 502 The server failed to request media from the media repo.
|
||||
const val REASON_MCS_MEDIA_REQUEST_FAILED = "MCS_MEDIA_REQUEST_FAILED"
|
||||
|
||||
/* 400 The server failed to decrypt the encrypted media downloaded from the media repo.*/
|
||||
const val REASON_MCS_MEDIA_FAILED_TO_DECRYPT = "MCS_MEDIA_FAILED_TO_DECRYPT"
|
||||
|
||||
/* 403 The server scanned the downloaded media but the antivirus script returned a non-zero exit code.*/
|
||||
const val REASON_MCS_MEDIA_NOT_CLEAN = "MCS_MEDIA_NOT_CLEAN"
|
||||
|
||||
/* 403 The provided encrypted_body could not be decrypted. The client should request the public key of the server and then retry (once).*/
|
||||
const val REASON_MCS_BAD_DECRYPTION = "MCS_BAD_DECRYPTION"
|
||||
|
||||
/* 400 The request body contains malformed JSON.*/
|
||||
const val REASON_MCS_MALFORMED_JSON = "MCS_MALFORMED_JSON"
|
||||
}
|
||||
}
|
||||
|
||||
class ScanFailure(val error: ContentScannerError, val httpCode: Int, cause: Throwable? = null) : Throwable(cause = cause)
|
||||
|
||||
// For Glide, which deals with Exception and not with Throwable
|
||||
fun ScanFailure.toException() = Exception(this)
|
||||
fun Throwable.toScanFailure() = this.cause as? ScanFailure
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.contentscanner
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
|
||||
|
||||
interface ContentScannerService {
|
||||
|
||||
val serverPublicKey: String?
|
||||
|
||||
fun getContentScannerServer(): String?
|
||||
fun setScannerUrl(url: String?)
|
||||
fun enableScanner(enabled: Boolean)
|
||||
fun isScannerEnabled(): Boolean
|
||||
fun getLiveStatusForFile(mxcUrl: String, fetchIfNeeded: Boolean = true, fileInfo: ElementToDecrypt? = null): LiveData<Optional<ScanStatusInfo>>
|
||||
fun getCachedScanResultForFile(mxcUrl: String): ScanStatusInfo?
|
||||
|
||||
/**
|
||||
* Get the current public curve25519 key that the AV server is advertising.
|
||||
* @param callback on success callback containing the server public key
|
||||
*/
|
||||
suspend fun getServerPublicKey(forceDownload: Boolean = false): String?
|
||||
suspend fun getScanResultForAttachment(mxcUrl: String, fileInfo: ElementToDecrypt? = null): ScanStatusInfo
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.contentscanner
|
||||
|
||||
enum class ScanState {
|
||||
TRUSTED,
|
||||
INFECTED,
|
||||
UNKNOWN,
|
||||
IN_PROGRESS
|
||||
}
|
||||
|
||||
data class ScanStatusInfo(
|
||||
val state: ScanState,
|
||||
val scanDateTimestamp: Long?,
|
||||
val humanReadableMessage: String?
|
||||
)
|
|
@ -28,6 +28,10 @@ object RelationType {
|
|||
/** Lets you define an event which references an existing event.*/
|
||||
const val REFERENCE = "m.reference"
|
||||
|
||||
/** Lets you define an thread event that belongs to another existing event.*/
|
||||
// const val THREAD = "m.thread" // m.thread is not yet released in the backend
|
||||
const val THREAD = "io.element.thread" // io.element.thread will be replaced by m.thread when it is released
|
||||
|
||||
/** Lets you define an event which adds a response to an existing event.*/
|
||||
const val RESPONSE = "org.matrix.response"
|
||||
}
|
||||
|
|
|
@ -23,15 +23,15 @@ package org.matrix.android.sdk.api.session.room.send
|
|||
* EDIT: draft of an edition of a message
|
||||
* REPLY: draft of a reply of another message
|
||||
*/
|
||||
sealed class UserDraft(open val text: String) {
|
||||
data class REGULAR(override val text: String) : UserDraft(text)
|
||||
data class QUOTE(val linkedEventId: String, override val text: String) : UserDraft(text)
|
||||
data class EDIT(val linkedEventId: String, override val text: String) : UserDraft(text)
|
||||
data class REPLY(val linkedEventId: String, override val text: String) : UserDraft(text)
|
||||
sealed interface UserDraft {
|
||||
data class Regular(val text: String) : UserDraft
|
||||
data class Quote(val linkedEventId: String, val text: String) : UserDraft
|
||||
data class Edit(val linkedEventId: String, val text: String) : UserDraft
|
||||
data class Reply(val linkedEventId: String, val text: String) : UserDraft
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return when (this) {
|
||||
is REGULAR -> text.isNotBlank()
|
||||
is Regular -> text.isNotBlank()
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,10 @@ import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.util.ContentUtils
|
||||
import org.matrix.android.sdk.api.util.ContentUtils.extractUsefulTextFromReply
|
||||
|
||||
/**
|
||||
|
@ -135,20 +137,6 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last Message body, after a possible edition
|
||||
*/
|
||||
fun TimelineEvent.getLastMessageBody(): String? {
|
||||
val lastMessageContent = getLastMessageContent()
|
||||
|
||||
if (lastMessageContent != null) {
|
||||
return lastMessageContent.newContent?.toModel<MessageContent>()?.body
|
||||
?: lastMessageContent.body
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it's a reply
|
||||
*/
|
||||
|
@ -160,11 +148,25 @@ fun TimelineEvent.isEdition(): Boolean {
|
|||
return root.isEdition()
|
||||
}
|
||||
|
||||
fun TimelineEvent.getTextEditableContent(): String? {
|
||||
val lastContent = getLastMessageContent()
|
||||
/**
|
||||
* Get the latest message body, after a possible edition, stripping the reply prefix if necessary
|
||||
*/
|
||||
fun TimelineEvent.getTextEditableContent(): String {
|
||||
val lastContentBody = getLastMessageContent()?.body ?: return ""
|
||||
return if (isReply()) {
|
||||
return extractUsefulTextFromReply(lastContent?.body ?: "")
|
||||
extractUsefulTextFromReply(lastContentBody)
|
||||
} else {
|
||||
lastContent?.body ?: ""
|
||||
lastContentBody
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest displayable content.
|
||||
* Will take care to hide spoiler text
|
||||
*/
|
||||
fun MessageContent.getTextDisplayableContent(): String {
|
||||
return newContent?.toModel<MessageTextContent>()?.matrixFormattedBody?.let { ContentUtils.formatSpoilerTextFromHtml(it) }
|
||||
?: newContent?.toModel<MessageContent>()?.body
|
||||
?: (this as MessageTextContent?)?.matrixFormattedBody?.let { ContentUtils.formatSpoilerTextFromHtml(it) }
|
||||
?: body
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.matrix.android.sdk.api.util
|
||||
|
||||
import org.matrix.android.sdk.internal.util.unescapeHtml
|
||||
|
||||
object ContentUtils {
|
||||
fun extractUsefulTextFromReply(repliedBody: String): String {
|
||||
val lines = repliedBody.lines()
|
||||
|
@ -44,4 +46,15 @@ object ContentUtils {
|
|||
}
|
||||
return repliedBody
|
||||
}
|
||||
|
||||
@Suppress("RegExpRedundantEscape")
|
||||
fun formatSpoilerTextFromHtml(formattedBody: String): String {
|
||||
// var reason = "",
|
||||
// can capture the spoiler reason for better formatting? ex. { reason = it.value; ">"}
|
||||
return formattedBody.replace("(?<=<span data-mx-spoiler)=\\\".+?\\\">".toRegex(), ">")
|
||||
.replace("(?<=<span data-mx-spoiler>).+?(?=</span>)".toRegex()) { SPOILER_CHAR.repeat(it.value.length) }
|
||||
.unescapeHtml()
|
||||
}
|
||||
|
||||
private const val SPOILER_CHAR = "█"
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistration
|
|||
import org.matrix.android.sdk.internal.auth.registration.RegisterAddThreePidTask
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.content.DefaultContentUrlResolver
|
||||
import org.matrix.android.sdk.internal.session.contentscanner.DisabledContentScannerService
|
||||
|
||||
internal class DefaultLoginWizard(
|
||||
private val authAPI: AuthAPI,
|
||||
|
@ -44,7 +45,7 @@ internal class DefaultLoginWizard(
|
|||
|
||||
private val getProfileTask: GetProfileTask = DefaultGetProfileTask(
|
||||
authAPI,
|
||||
DefaultContentUrlResolver(pendingSessionData.homeServerConnectionConfig)
|
||||
DefaultContentUrlResolver(pendingSessionData.homeServerConnectionConfig, DisabledContentScannerService())
|
||||
)
|
||||
|
||||
override suspend fun getProfileInfo(matrixId: String): LoginProfileInfo {
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
|||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||
|
@ -110,6 +111,9 @@ import kotlin.math.max
|
|||
* CryptoService maintains all necessary keys and their sharing with other devices required for the crypto.
|
||||
* Specially, it tracks all room membership changes events in order to do keys updates.
|
||||
*/
|
||||
|
||||
private val loggerTag = LoggerTag("DefaultCryptoService", LoggerTag.CRYPTO)
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultCryptoService @Inject constructor(
|
||||
// Olm Manager
|
||||
|
@ -346,7 +350,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
deviceListManager.startTrackingDeviceList(listOf(userId))
|
||||
deviceListManager.refreshOutdatedDeviceLists()
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## CRYPTO onSyncWillProcess ")
|
||||
Timber.tag(loggerTag.value).e(failure, "onSyncWillProcess ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +383,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
{
|
||||
isStarting.set(false)
|
||||
isStarted.set(false)
|
||||
Timber.e(it, "Start failed")
|
||||
Timber.tag(loggerTag.value).e(it, "Start failed")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -551,14 +555,14 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
|
||||
|
||||
if (!existingAlgorithm.isNullOrEmpty() && existingAlgorithm != algorithm) {
|
||||
Timber.e("## CRYPTO | setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
|
||||
Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
|
||||
return false
|
||||
}
|
||||
|
||||
val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm)
|
||||
|
||||
if (!encryptingClass) {
|
||||
Timber.e("## CRYPTO | setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
|
||||
Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -577,7 +581,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
// e2e rooms with them, so there is room for optimisation here, but for now
|
||||
// we just invalidate everyone in the room.
|
||||
if (null == existingAlgorithm) {
|
||||
Timber.v("Enabling encryption in $roomId for the first time; invalidating device lists for all users therein")
|
||||
Timber.tag(loggerTag.value).d("Enabling encryption in $roomId for the first time; invalidating device lists for all users therein")
|
||||
|
||||
val userIds = ArrayList(membersId)
|
||||
|
||||
|
@ -655,17 +659,17 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
val safeAlgorithm = alg
|
||||
if (safeAlgorithm != null) {
|
||||
val t0 = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | encryptEventContent() starts")
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent() starts")
|
||||
runCatching {
|
||||
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||
Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.tag(loggerTag.value).v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
|
||||
}.foldToCallback(callback)
|
||||
} else {
|
||||
val algorithm = getEncryptionAlgorithm(roomId)
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON,
|
||||
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
|
||||
Timber.e("## CRYPTO | encryptEventContent() : $reason")
|
||||
Timber.tag(loggerTag.value).e("encryptEventContent() : failed $reason")
|
||||
callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_ENCRYPT, reason)))
|
||||
}
|
||||
}
|
||||
|
@ -677,7 +681,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
if (roomEncryptor is IMXGroupEncryption) {
|
||||
roomEncryptor.discardSessionKey()
|
||||
} else {
|
||||
Timber.e("## CRYPTO | discardOutboundSession() for:$roomId: Unable to handle IMXGroupEncryption")
|
||||
Timber.tag(loggerTag.value).e("discardOutboundSession() for:$roomId: Unable to handle IMXGroupEncryption")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -768,14 +772,14 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
*/
|
||||
private fun onRoomKeyEvent(event: Event) {
|
||||
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
|
||||
Timber.i("## CRYPTO | onRoomKeyEvent() from: ${event.senderId} type<${event.getClearType()}> , sessionId<${roomKeyContent.sessionId}>")
|
||||
Timber.tag(loggerTag.value).i("onRoomKeyEvent() from: ${event.senderId} type<${event.getClearType()}> , sessionId<${roomKeyContent.sessionId}>")
|
||||
if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.algorithm.isNullOrEmpty()) {
|
||||
Timber.e("## CRYPTO | onRoomKeyEvent() : missing fields")
|
||||
Timber.tag(loggerTag.value).e("onRoomKeyEvent() : missing fields")
|
||||
return
|
||||
}
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
|
||||
if (alg == null) {
|
||||
Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
|
||||
Timber.tag(loggerTag.value).e("GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
|
||||
return
|
||||
}
|
||||
alg.onRoomKeyEvent(event, keysBackupService)
|
||||
|
@ -783,29 +787,29 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
private fun onKeyWithHeldReceived(event: Event) {
|
||||
val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also {
|
||||
Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
|
||||
Timber.tag(loggerTag.value).i("Malformed onKeyWithHeldReceived() : missing fields")
|
||||
}
|
||||
Timber.i("## CRYPTO | onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>")
|
||||
Timber.tag(loggerTag.value).i("onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>")
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm)
|
||||
if (alg is IMXWithHeldExtension) {
|
||||
alg.onRoomKeyWithHeldEvent(withHeldContent)
|
||||
} else {
|
||||
Timber.e("## CRYPTO | onKeyWithHeldReceived() from:${event.senderId}: Unable to handle WithHeldContent for ${withHeldContent.algorithm}")
|
||||
Timber.tag(loggerTag.value).e("onKeyWithHeldReceived() from:${event.senderId}: Unable to handle WithHeldContent for ${withHeldContent.algorithm}")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSecretSendReceived(event: Event) {
|
||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||
Timber.tag(loggerTag.value).i("GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||
if (!event.isEncrypted()) {
|
||||
// secret send messages must be encrypted
|
||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() :Received unencrypted secret send event")
|
||||
Timber.tag(loggerTag.value).e("GOSSIP onSecretSend() :Received unencrypted secret send event")
|
||||
return
|
||||
}
|
||||
|
||||
// Was that sent by us?
|
||||
if (event.senderId != userId) {
|
||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
|
||||
Timber.tag(loggerTag.value).e("GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -815,13 +819,13 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
.getOutgoingSecretKeyRequests().firstOrNull { it.requestId == secretContent.requestId }
|
||||
|
||||
if (existingRequest == null) {
|
||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
|
||||
Timber.tag(loggerTag.value).i("GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
|
||||
return
|
||||
}
|
||||
|
||||
if (!handleSDKLevelGossip(existingRequest.secretName, secretContent.secretValue)) {
|
||||
// TODO Ask to application layer?
|
||||
Timber.v("## CRYPTO | onSecretSend() : secret not handled by SDK")
|
||||
Timber.tag(loggerTag.value).v("onSecretSend() : secret not handled by SDK")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -858,7 +862,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
||||
if (!event.isStateEvent()) {
|
||||
// Ignore
|
||||
Timber.w("Invalid encryption event")
|
||||
Timber.tag(loggerTag.value).w("Invalid encryption event")
|
||||
return
|
||||
}
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
|
@ -912,7 +916,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
*/
|
||||
private suspend fun uploadDeviceKeys() {
|
||||
if (cryptoStore.areDeviceKeysUploaded()) {
|
||||
Timber.d("Keys already uploaded, nothing to do")
|
||||
Timber.tag(loggerTag.value).d("Keys already uploaded, nothing to do")
|
||||
return
|
||||
}
|
||||
// Prepare the device keys data to send
|
||||
|
@ -971,13 +975,13 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
password: String,
|
||||
progressListener: ProgressListener?): ImportRoomKeysResult {
|
||||
return withContext(coroutineDispatchers.crypto) {
|
||||
Timber.v("## CRYPTO | importRoomKeys starts")
|
||||
Timber.tag(loggerTag.value).v("importRoomKeys starts")
|
||||
|
||||
val t0 = System.currentTimeMillis()
|
||||
val roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password)
|
||||
val t1 = System.currentTimeMillis()
|
||||
|
||||
Timber.v("## CRYPTO | importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
||||
Timber.tag(loggerTag.value).v("importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
||||
|
||||
val importedSessions = MoshiProvider.providesMoshi()
|
||||
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
|
||||
|
@ -985,7 +989,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
val t2 = System.currentTimeMillis()
|
||||
|
||||
Timber.v("## CRYPTO | importRoomKeys : JSON parsing ${t2 - t1} ms")
|
||||
Timber.tag(loggerTag.value).v("importRoomKeys : JSON parsing ${t2 - t1} ms")
|
||||
|
||||
if (importedSessions == null) {
|
||||
throw Exception("Error")
|
||||
|
@ -1122,7 +1126,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
*/
|
||||
override fun reRequestRoomKeyForEvent(event: Event) {
|
||||
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.e("## CRYPTO | reRequestRoomKeyForEvent Failed to re-request key, null content")
|
||||
Timber.tag(loggerTag.value).e("reRequestRoomKeyForEvent Failed to re-request key, null content")
|
||||
}
|
||||
|
||||
val requestBody = RoomKeyRequestBody(
|
||||
|
@ -1137,7 +1141,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
override fun requestRoomKeyForEvent(event: Event) {
|
||||
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.e("## CRYPTO | requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
|
||||
Timber.tag(loggerTag.value).e("requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
|
||||
}
|
||||
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
|
@ -1148,7 +1152,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
roomDecryptorProvider
|
||||
.getOrCreateRoomDecryptor(event.roomId, wireContent.algorithm)
|
||||
?.requestKeysForEvent(event, false) ?: run {
|
||||
Timber.v("## CRYPTO | requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
|
||||
Timber.tag(loggerTag.value).v("requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1287,12 +1291,12 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
override fun prepareToEncrypt(roomId: String, callback: MatrixCallback<Unit>) {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
Timber.d("## CRYPTO | prepareToEncrypt() : Check room members up to date")
|
||||
Timber.tag(loggerTag.value).d("prepareToEncrypt() roomId:$roomId Check room members up to date")
|
||||
// Ensure to load all room members
|
||||
try {
|
||||
loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() : Failed to load room members")
|
||||
Timber.tag(loggerTag.value).e("prepareToEncrypt() : Failed to load room members")
|
||||
callback.onFailure(failure)
|
||||
return@launch
|
||||
}
|
||||
|
@ -1305,7 +1309,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
if (alg == null) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, MXCryptoError.NO_MORE_ALGORITHM_REASON)
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() : $reason")
|
||||
Timber.tag(loggerTag.value).e("prepareToEncrypt() : $reason")
|
||||
callback.onFailure(IllegalArgumentException("Missing algorithm"))
|
||||
return@launch
|
||||
}
|
||||
|
@ -1315,7 +1319,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
}.fold(
|
||||
{ callback.onSuccess(Unit) },
|
||||
{
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() failed.")
|
||||
Timber.tag(loggerTag.value).e(it, "prepareToEncrypt() failed.")
|
||||
callback.onFailure(it)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -16,17 +16,21 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.crypto.actions
|
||||
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXKey
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXOlmSessionResult
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.toDebugString
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val ONE_TIME_KEYS_RETRY_COUNT = 3
|
||||
|
||||
private val loggerTag = LoggerTag("EnsureOlmSessionsForDevicesAction", LoggerTag.CRYPTO)
|
||||
|
||||
internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) {
|
||||
|
@ -36,15 +40,22 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||
|
||||
val results = MXUsersDevicesMap<MXOlmSessionResult>()
|
||||
|
||||
for ((userId, deviceInfos) in devicesByUser) {
|
||||
for (deviceInfo in deviceInfos) {
|
||||
for ((userId, deviceList) in devicesByUser) {
|
||||
for (deviceInfo in deviceList) {
|
||||
val deviceId = deviceInfo.deviceId
|
||||
val key = deviceInfo.identityKey()
|
||||
if (key == null) {
|
||||
Timber.w("## CRYPTO | Ignoring device (${deviceInfo.userId}|$deviceId) without identity key")
|
||||
continue
|
||||
}
|
||||
|
||||
val sessionId = olmDevice.getSessionId(key!!)
|
||||
val sessionId = olmDevice.getSessionId(key)
|
||||
|
||||
if (sessionId.isNullOrEmpty() || force) {
|
||||
Timber.tag(loggerTag.value).d("Found no existing olm session (${deviceInfo.userId}|$deviceId) (force=$force)")
|
||||
devicesWithoutSession.add(deviceInfo)
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("using olm session $sessionId for (${deviceInfo.userId}|$deviceId)")
|
||||
}
|
||||
|
||||
val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId)
|
||||
|
@ -52,6 +63,8 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
Timber.tag(loggerTag.value).d("Devices without olm session (count:${devicesWithoutSession.size}) :" +
|
||||
" ${devicesWithoutSession.joinToString { "${it.userId}|${it.deviceId}" }}")
|
||||
if (devicesWithoutSession.size == 0) {
|
||||
return results
|
||||
}
|
||||
|
@ -71,11 +84,11 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||
//
|
||||
// That should eventually resolve itself, but it's poor form.
|
||||
|
||||
Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
||||
Timber.tag(loggerTag.value).i("claimOneTimeKeysForUsersDevices() : ${usersDevicesToClaim.toDebugString()}")
|
||||
|
||||
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
||||
val oneTimeKeys = oneTimeKeysForUsersDeviceTask.executeRetry(claimParams, remainingRetry = ONE_TIME_KEYS_RETRY_COUNT)
|
||||
Timber.v("## CRYPTO | claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys")
|
||||
Timber.tag(loggerTag.value).v("claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys")
|
||||
for ((userId, deviceInfos) in devicesByUser) {
|
||||
for (deviceInfo in deviceInfos) {
|
||||
var oneTimeKey: MXKey? = null
|
||||
|
@ -83,7 +96,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||
if (null != deviceIds) {
|
||||
for (deviceId in deviceIds) {
|
||||
val olmSessionResult = results.getObject(userId, deviceId)
|
||||
if (olmSessionResult!!.sessionId != null && !force) {
|
||||
if (olmSessionResult?.sessionId != null && !force) {
|
||||
// We already have a result for this device
|
||||
continue
|
||||
}
|
||||
|
@ -92,12 +105,11 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||
oneTimeKey = key
|
||||
}
|
||||
if (oneTimeKey == null) {
|
||||
Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm +
|
||||
" for device " + userId + " : " + deviceId)
|
||||
Timber.tag(loggerTag.value).d("No one time key for $userId|$deviceId")
|
||||
continue
|
||||
}
|
||||
// Update the result for this device in results
|
||||
olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
|
||||
olmSessionResult?.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,31 +124,36 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
|
|||
val signKeyId = "ed25519:$deviceId"
|
||||
val signature = oneTimeKey.signatureForUserId(userId, signKeyId)
|
||||
|
||||
if (!signature.isNullOrEmpty() && !deviceInfo.fingerprint().isNullOrEmpty()) {
|
||||
val fingerprint = deviceInfo.fingerprint()
|
||||
if (!signature.isNullOrEmpty() && !fingerprint.isNullOrEmpty()) {
|
||||
var isVerified = false
|
||||
var errorMessage: String? = null
|
||||
|
||||
try {
|
||||
olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
|
||||
olmDevice.verifySignature(fingerprint, oneTimeKey.signalableJSONDictionary(), signature)
|
||||
isVerified = true
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(loggerTag.value).d(e, "verifyKeyAndStartSession() : Verify error for otk: ${oneTimeKey.signalableJSONDictionary()}," +
|
||||
" signature:$signature fingerprint:$fingerprint")
|
||||
Timber.tag(loggerTag.value).e("verifyKeyAndStartSession() : Verify error for ${deviceInfo.userId}|${deviceInfo.deviceId} " +
|
||||
" - signable json ${oneTimeKey.signalableJSONDictionary()}")
|
||||
errorMessage = e.message
|
||||
}
|
||||
|
||||
// Check one-time key signature
|
||||
if (isVerified) {
|
||||
sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)
|
||||
sessionId = deviceInfo.identityKey()?.let { identityKey ->
|
||||
olmDevice.createOutboundSession(identityKey, oneTimeKey.value)
|
||||
}
|
||||
|
||||
if (!sessionId.isNullOrEmpty()) {
|
||||
Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId +
|
||||
" for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
|
||||
} else {
|
||||
if (sessionId.isNullOrEmpty()) {
|
||||
// Possibly a bad key
|
||||
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).e("verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("verifyKeyAndStartSession() : Started new sessionId $sessionId for device $userId:$deviceId")
|
||||
}
|
||||
} else {
|
||||
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId +
|
||||
":" + deviceId + " Error " + errorMessage)
|
||||
Timber.tag(loggerTag.value).e("verifyKeyAndStartSession() : Unable to verify otk signature for $userId:$deviceId: $errorMessage")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
|
@ -44,6 +45,8 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
|||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("MXMegolmDecryption", LoggerTag.CRYPTO)
|
||||
|
||||
internal class MXMegolmDecryption(private val userId: String,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
|
@ -74,7 +77,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
|
||||
@Throws(MXCryptoError::class)
|
||||
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult {
|
||||
Timber.v("## CRYPTO | decryptEvent ${event.eventId}, requestKeysOnFail:$requestKeysOnFail")
|
||||
Timber.tag(loggerTag.value).v("decryptEvent ${event.eventId}, requestKeysOnFail:$requestKeysOnFail")
|
||||
if (event.roomId.isNullOrBlank()) {
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
}
|
||||
|
@ -230,7 +233,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
* @param event the key event.
|
||||
*/
|
||||
override fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {
|
||||
Timber.v("## CRYPTO | onRoomKeyEvent()")
|
||||
Timber.tag(loggerTag.value).v("onRoomKeyEvent()")
|
||||
var exportFormat = false
|
||||
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
|
||||
|
||||
|
@ -239,11 +242,11 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
val forwardingCurve25519KeyChain: MutableList<String> = ArrayList()
|
||||
|
||||
if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.sessionId.isNullOrEmpty() || roomKeyContent.sessionKey.isNullOrEmpty()) {
|
||||
Timber.e("## CRYPTO | onRoomKeyEvent() : Key event is missing fields")
|
||||
Timber.tag(loggerTag.value).e("onRoomKeyEvent() : Key event is missing fields")
|
||||
return
|
||||
}
|
||||
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||
Timber.i("## CRYPTO | onRoomKeyEvent(), forward adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
Timber.tag(loggerTag.value).i("onRoomKeyEvent(), forward adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()
|
||||
?: return
|
||||
|
||||
|
@ -252,7 +255,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
}
|
||||
|
||||
if (senderKey == null) {
|
||||
Timber.e("## CRYPTO | onRoomKeyEvent() : event is missing sender_key field")
|
||||
Timber.tag(loggerTag.value).e("onRoomKeyEvent() : event is missing sender_key field")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -261,20 +264,20 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
exportFormat = true
|
||||
senderKey = forwardedRoomKeyContent.senderKey
|
||||
if (null == senderKey) {
|
||||
Timber.e("## CRYPTO | onRoomKeyEvent() : forwarded_room_key event is missing sender_key field")
|
||||
Timber.tag(loggerTag.value).e("onRoomKeyEvent() : forwarded_room_key event is missing sender_key field")
|
||||
return
|
||||
}
|
||||
|
||||
if (null == forwardedRoomKeyContent.senderClaimedEd25519Key) {
|
||||
Timber.e("## CRYPTO | forwarded_room_key_event is missing sender_claimed_ed25519_key field")
|
||||
Timber.tag(loggerTag.value).e("forwarded_room_key_event is missing sender_claimed_ed25519_key field")
|
||||
return
|
||||
}
|
||||
|
||||
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key
|
||||
} else {
|
||||
Timber.i("## CRYPTO | onRoomKeyEvent(), Adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
Timber.tag(loggerTag.value).i("onRoomKeyEvent(), Adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
if (null == senderKey) {
|
||||
Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
|
||||
Timber.tag(loggerTag.value).e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -282,7 +285,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
keysClaimed = event.getKeysClaimed().toMutableMap()
|
||||
}
|
||||
|
||||
Timber.i("## CRYPTO | onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}")
|
||||
Timber.tag(loggerTag.value).i("onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}")
|
||||
val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId,
|
||||
roomKeyContent.sessionKey,
|
||||
roomKeyContent.roomId,
|
||||
|
@ -314,7 +317,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
* @param sessionId the session id
|
||||
*/
|
||||
override fun onNewSession(senderKey: String, sessionId: String) {
|
||||
Timber.v(" CRYPTO | ON NEW SESSION $sessionId - $senderKey")
|
||||
Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey")
|
||||
newSessionListener?.onNewSession(null, senderKey, sessionId)
|
||||
}
|
||||
|
||||
|
@ -346,10 +349,10 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
if (olmSessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
Timber.e("no session with this device $deviceId, probably because there were no one-time keys.")
|
||||
Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.")
|
||||
return@mapCatching
|
||||
}
|
||||
Timber.i("## CRYPTO | shareKeysWithDevice() : sharing session ${body.sessionId} with device $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${body.sessionId} with device $userId:$deviceId")
|
||||
|
||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
||||
runCatching { olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) }
|
||||
|
@ -360,7 +363,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
},
|
||||
{
|
||||
// TODO
|
||||
Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body")
|
||||
Timber.tag(loggerTag.value).e(it, "shareKeysWithDevice: failed to get session for request $body")
|
||||
}
|
||||
|
||||
)
|
||||
|
@ -368,12 +371,12 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.i("## CRYPTO | shareKeysWithDevice() : sending ${body.sessionId} to $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${body.sessionId} to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## CRYPTO | shareKeysWithDevice() : Failed to send ${body.sessionId} to $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${body.sessionId} to $userId:$deviceId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
@ -36,6 +37,8 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
|||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
|
||||
import org.matrix.android.sdk.internal.crypto.model.forEach
|
||||
import org.matrix.android.sdk.internal.crypto.model.toDebugCount
|
||||
import org.matrix.android.sdk.internal.crypto.model.toDebugString
|
||||
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
|
@ -43,6 +46,8 @@ import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
|||
import org.matrix.android.sdk.internal.util.convertToUTF8
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("MXMegolmEncryption", LoggerTag.CRYPTO)
|
||||
|
||||
internal class MXMegolmEncryption(
|
||||
// The id of the room we will be sending to.
|
||||
private val roomId: String,
|
||||
|
@ -51,8 +56,8 @@ internal class MXMegolmEncryption(
|
|||
private val cryptoStore: IMXCryptoStore,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||
private val userId: String,
|
||||
private val deviceId: String,
|
||||
private val myUserId: String,
|
||||
private val myDeviceId: String,
|
||||
private val sendToDeviceTask: SendToDeviceTask,
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
||||
|
@ -80,9 +85,10 @@ internal class MXMegolmEncryption(
|
|||
eventType: String,
|
||||
userIds: List<String>): Content {
|
||||
val ts = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent : getDevicesInRoom")
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
|
||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId - devices count in room ${devices.allowedDevices.toDebugCount()}")
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
|
||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||
|
||||
return encryptContent(outboundSession, eventType, eventContent)
|
||||
|
@ -91,7 +97,7 @@ internal class MXMegolmEncryption(
|
|||
// annoyingly we have to serialize again the saved outbound session to store message index :/
|
||||
// if not we would see duplicate message index errors
|
||||
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
|
||||
Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis")
|
||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId Finished in ${System.currentTimeMillis() - ts} millis")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,13 +124,13 @@ internal class MXMegolmEncryption(
|
|||
|
||||
override suspend fun preshareKey(userIds: List<String>) {
|
||||
val ts = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | preshareKey : getDevicesInRoom")
|
||||
Timber.tag(loggerTag.value).d("preshareKey started in $roomId ...")
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||
|
||||
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
||||
|
||||
Timber.v("## CRYPTO | preshareKey ${System.currentTimeMillis() - ts} millis")
|
||||
Timber.tag(loggerTag.value).d("preshareKey in $roomId done in ${System.currentTimeMillis() - ts} millis")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +139,7 @@ internal class MXMegolmEncryption(
|
|||
* @return the session description
|
||||
*/
|
||||
private fun prepareNewSessionInRoom(): MXOutboundSessionInfo {
|
||||
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
|
||||
Timber.tag(loggerTag.value).v("prepareNewSessionInRoom() ")
|
||||
val sessionId = olmDevice.createOutboundGroupSessionForRoom(roomId)
|
||||
|
||||
val keysClaimedMap = HashMap<String, String>()
|
||||
|
@ -153,13 +159,14 @@ internal class MXMegolmEncryption(
|
|||
* @param devicesInRoom the devices list
|
||||
*/
|
||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
|
||||
Timber.v("## CRYPTO | ensureOutboundSession start")
|
||||
Timber.tag(loggerTag.value).v("ensureOutboundSession roomId:$roomId")
|
||||
var session = outboundSession
|
||||
if (session == null ||
|
||||
// Need to make a brand new session?
|
||||
session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) ||
|
||||
// Determine if we have shared with anyone we shouldn't have
|
||||
session.sharedWithTooManyDevices(devicesInRoom)) {
|
||||
Timber.tag(loggerTag.value).d("roomId:$roomId Starting new megolm session because we need to rotate.")
|
||||
session = prepareNewSessionInRoom()
|
||||
outboundSession = session
|
||||
}
|
||||
|
@ -176,6 +183,8 @@ internal class MXMegolmEncryption(
|
|||
}
|
||||
}
|
||||
}
|
||||
val devicesCount = shareMap.entries.fold(0) { acc, new -> acc + new.value.size }
|
||||
Timber.tag(loggerTag.value).d("roomId:$roomId found $devicesCount devices without megolm session(${session.sessionId})")
|
||||
shareKey(safeSession, shareMap)
|
||||
return safeSession
|
||||
}
|
||||
|
@ -190,7 +199,7 @@ internal class MXMegolmEncryption(
|
|||
devicesByUsers: Map<String, List<CryptoDeviceInfo>>) {
|
||||
// nothing to send, the task is done
|
||||
if (devicesByUsers.isEmpty()) {
|
||||
Timber.v("## CRYPTO | shareKey() : nothing more to do")
|
||||
Timber.tag(loggerTag.value).v("shareKey() : nothing more to do")
|
||||
return
|
||||
}
|
||||
// reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user)
|
||||
|
@ -203,7 +212,7 @@ internal class MXMegolmEncryption(
|
|||
break
|
||||
}
|
||||
}
|
||||
Timber.v("## CRYPTO | shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
|
||||
Timber.tag(loggerTag.value).v("shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
|
||||
shareUserDevicesKey(session, subMap)
|
||||
val remainingDevices = devicesByUsers - subMap.keys
|
||||
shareKey(session, remainingDevices)
|
||||
|
@ -232,11 +241,11 @@ internal class MXMegolmEncryption(
|
|||
payload["content"] = submap
|
||||
|
||||
var t0 = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
|
||||
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts")
|
||||
|
||||
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
Timber.v(
|
||||
"""## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
||||
Timber.tag(loggerTag.value).v(
|
||||
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
||||
.trimMargin()
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
|
@ -254,10 +263,11 @@ internal class MXMegolmEncryption(
|
|||
// MSC 2399
|
||||
// send withheld m.no_olm: an olm session could not be established.
|
||||
// This may happen, for example, if the sender was unable to obtain a one-time key from the recipient.
|
||||
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : No Olm Session for $userId:$deviceID mark for withheld")
|
||||
noOlmToNotify.add(UserDevice(userId, deviceID))
|
||||
continue
|
||||
}
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
|
||||
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
|
||||
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo)))
|
||||
haveTargets = true
|
||||
}
|
||||
|
@ -275,7 +285,7 @@ internal class MXMegolmEncryption(
|
|||
gossipingEventBuffer.add(
|
||||
Event(
|
||||
type = EventType.ROOM_KEY,
|
||||
senderId = this.userId,
|
||||
senderId = myUserId,
|
||||
content = submap.apply {
|
||||
this["session_key"] = ""
|
||||
// we add a fake key for trail
|
||||
|
@ -289,17 +299,18 @@ internal class MXMegolmEncryption(
|
|||
|
||||
if (haveTargets) {
|
||||
t0 = System.currentTimeMillis()
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
|
||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() ${session.sessionId} : has target")
|
||||
Timber.tag(loggerTag.value).d("sending to device room key for ${session.sessionId} to ${contentMap.toDebugString()}")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||
try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
} catch (failure: Throwable) {
|
||||
// What to do here...
|
||||
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
|
||||
Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
|
||||
}
|
||||
} else {
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
|
||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() : no need to share key")
|
||||
}
|
||||
|
||||
if (noOlmToNotify.isNotEmpty()) {
|
||||
|
@ -317,7 +328,8 @@ internal class MXMegolmEncryption(
|
|||
sessionId: String,
|
||||
senderKey: String?,
|
||||
code: WithHeldCode) {
|
||||
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code")
|
||||
Timber.tag(loggerTag.value).d("notifyKeyWithHeld() :sending withheld for session:$sessionId and code $code to" +
|
||||
" ${targets.joinToString { "${it.userId}|${it.deviceId}" }}")
|
||||
val withHeldContent = RoomKeyWithHeldContent(
|
||||
roomId = roomId,
|
||||
senderKey = senderKey,
|
||||
|
@ -336,7 +348,7 @@ internal class MXMegolmEncryption(
|
|||
try {
|
||||
sendToDeviceTask.execute(params)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ")
|
||||
Timber.tag(loggerTag.value).e("notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,7 +375,7 @@ internal class MXMegolmEncryption(
|
|||
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
map["device_id"] = deviceId
|
||||
map["device_id"] = myDeviceId
|
||||
session.useCount++
|
||||
return map
|
||||
}
|
||||
|
@ -424,9 +436,9 @@ internal class MXMegolmEncryption(
|
|||
userId: String,
|
||||
deviceId: String,
|
||||
senderKey: String): Boolean {
|
||||
Timber.i("## Crypto process reshareKey for $sessionId to $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).i("process reshareKey for $sessionId to $userId:$deviceId")
|
||||
val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) ?: return false
|
||||
.also { Timber.w("## Crypto reshareKey: Device not found") }
|
||||
.also { Timber.tag(loggerTag.value).w("reshareKey: Device not found") }
|
||||
|
||||
// Get the chain index of the key we previously sent this device
|
||||
val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, deviceInfo)
|
||||
|
@ -434,13 +446,13 @@ internal class MXMegolmEncryption(
|
|||
// This session was never shared with this user
|
||||
// Send a room key with held
|
||||
notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED)
|
||||
Timber.w("## Crypto reshareKey: ERROR : Never shared megolm with this device")
|
||||
Timber.tag(loggerTag.value).w("reshareKey: ERROR : Never shared megolm with this device")
|
||||
return false
|
||||
}
|
||||
// if found chain index should not be null
|
||||
val chainIndex = wasSessionSharedWithUser.chainIndex ?: return false
|
||||
.also {
|
||||
Timber.w("## Crypto reshareKey: Null chain index")
|
||||
Timber.tag(loggerTag.value).w("reshareKey: Null chain index")
|
||||
}
|
||||
|
||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||
|
@ -449,10 +461,10 @@ internal class MXMegolmEncryption(
|
|||
olmSessionResult?.sessionId // no session with this device, probably because there were no one-time keys.
|
||||
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
|
||||
?: return false.also {
|
||||
Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys")
|
||||
Timber.tag(loggerTag.value).w("reshareKey: no session with this device, probably because there were no one-time keys")
|
||||
}
|
||||
|
||||
Timber.i("[MXMegolmEncryption] reshareKey: sharing keys for session $senderKey|$sessionId:$chainIndex with device $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).i(" reshareKey: sharing keys for session $senderKey|$sessionId:$chainIndex with device $userId:$deviceId")
|
||||
|
||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
||||
|
||||
|
@ -464,7 +476,7 @@ internal class MXMegolmEncryption(
|
|||
},
|
||||
{
|
||||
// TODO
|
||||
Timber.e(it, "[MXMegolmEncryption] reshareKey: failed to get session $sessionId|$senderKey|$roomId")
|
||||
Timber.tag(loggerTag.value).e(it, "reshareKey: failed to get session $sessionId|$senderKey|$roomId")
|
||||
}
|
||||
|
||||
)
|
||||
|
@ -472,14 +484,14 @@ internal class MXMegolmEncryption(
|
|||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).i("reshareKey() : sending session $sessionId to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
return try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
Timber.i("## CRYPTO reshareKey() : successfully send <$sessionId> to $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).i("reshareKey() : successfully send <$sessionId> to $userId:$deviceId")
|
||||
true
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## CRYPTO reshareKey() : fail to send <$sessionId> to $userId:$deviceId")
|
||||
Timber.tag(loggerTag.value).e(failure, "reshareKey() : fail to send <$sessionId> to $userId:$deviceId")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
|
|||
cryptoStore = cryptoStore,
|
||||
deviceListManager = deviceListManager,
|
||||
ensureOlmSessionsForDevicesAction = ensureOlmSessionsForDevicesAction,
|
||||
userId = userId,
|
||||
deviceId = deviceId!!,
|
||||
myUserId = userId,
|
||||
myDeviceId = deviceId!!,
|
||||
sendToDeviceTask = sendToDeviceTask,
|
||||
messageEncrypter = messageEncrypter,
|
||||
warnOnUnknownDevicesRepository = warnOnUnknownDevicesRepository,
|
||||
|
|
|
@ -129,3 +129,11 @@ inline fun <T> MXUsersDevicesMap<T>.forEach(action: (String, String, T) -> Unit)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T> MXUsersDevicesMap<T>.toDebugString() =
|
||||
map.entries.joinToString { "${it.key} [${it.value.keys.joinToString { it }}]" }
|
||||
|
||||
internal fun <T> MXUsersDevicesMap<T>.toDebugCount() =
|
||||
map.entries.fold(0) { acc, new ->
|
||||
acc + new.value.keys.size
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.session.room.RoomAPI
|
|||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SendEventTask : Task<SendEventTask.Params, String> {
|
||||
|
@ -60,7 +61,9 @@ internal class DefaultSendEventTask @Inject constructor(
|
|||
)
|
||||
}
|
||||
localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENT)
|
||||
return response.eventId
|
||||
return response.eventId.also {
|
||||
Timber.d("Event: $it just sent in ${params.event.roomId}")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// localEchoRepository.updateSendState(params.event.eventId!!, SendState.UNDELIVERED)
|
||||
throw e
|
||||
|
|
|
@ -26,20 +26,20 @@ internal object DraftMapper {
|
|||
|
||||
fun map(entity: DraftEntity): UserDraft {
|
||||
return when (entity.draftMode) {
|
||||
DraftEntity.MODE_REGULAR -> UserDraft.REGULAR(entity.content)
|
||||
DraftEntity.MODE_EDIT -> UserDraft.EDIT(entity.linkedEventId, entity.content)
|
||||
DraftEntity.MODE_QUOTE -> UserDraft.QUOTE(entity.linkedEventId, entity.content)
|
||||
DraftEntity.MODE_REPLY -> UserDraft.REPLY(entity.linkedEventId, entity.content)
|
||||
DraftEntity.MODE_REGULAR -> UserDraft.Regular(entity.content)
|
||||
DraftEntity.MODE_EDIT -> UserDraft.Edit(entity.linkedEventId, entity.content)
|
||||
DraftEntity.MODE_QUOTE -> UserDraft.Quote(entity.linkedEventId, entity.content)
|
||||
DraftEntity.MODE_REPLY -> UserDraft.Reply(entity.linkedEventId, entity.content)
|
||||
else -> null
|
||||
} ?: UserDraft.REGULAR("")
|
||||
} ?: UserDraft.Regular("")
|
||||
}
|
||||
|
||||
fun map(domain: UserDraft): DraftEntity {
|
||||
return when (domain) {
|
||||
is UserDraft.REGULAR -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_REGULAR, linkedEventId = "")
|
||||
is UserDraft.EDIT -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_EDIT, linkedEventId = domain.linkedEventId)
|
||||
is UserDraft.QUOTE -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_QUOTE, linkedEventId = domain.linkedEventId)
|
||||
is UserDraft.REPLY -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_REPLY, linkedEventId = domain.linkedEventId)
|
||||
is UserDraft.Regular -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_REGULAR, linkedEventId = "")
|
||||
is UserDraft.Edit -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_EDIT, linkedEventId = domain.linkedEventId)
|
||||
is UserDraft.Quote -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_QUOTE, linkedEventId = domain.linkedEventId)
|
||||
is UserDraft.Reply -> DraftEntity(content = domain.text, draftMode = DraftEntity.MODE_REPLY, linkedEventId = domain.linkedEventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.database.model
|
|||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
|
@ -56,10 +57,10 @@ internal open class EventEntity(@Index var eventId: String = "",
|
|||
|
||||
companion object
|
||||
|
||||
fun setDecryptionResult(result: MXEventDecryptionResult) {
|
||||
fun setDecryptionResult(result: MXEventDecryptionResult, clearEvent: JsonDict? = null) {
|
||||
assertIsManaged()
|
||||
val decryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
payload = clearEvent ?: result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
|
|
|
@ -37,3 +37,7 @@ internal annotation class CryptoDatabase
|
|||
@Qualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class IdentityDatabase
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class ContentScannerDatabase
|
||||
|
|
|
@ -38,6 +38,9 @@ internal object NetworkConstants {
|
|||
// Integration
|
||||
const val URI_INTEGRATION_MANAGER_PATH = "_matrix/integrations/v1/"
|
||||
|
||||
// Content scanner
|
||||
const val URI_API_PREFIX_PATH_MEDIA_PROXY_UNSTABLE = "_matrix/media_proxy/unstable/"
|
||||
|
||||
// Federation
|
||||
const val URI_FEDERATION_PATH = "_matrix/federation/v1/"
|
||||
}
|
||||
|
|
|
@ -23,8 +23,10 @@ import androidx.core.content.FileProvider
|
|||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.completeWith
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||
|
@ -118,12 +120,24 @@ internal class DefaultFileService @Inject constructor(
|
|||
val cachedFiles = getFiles(url, fileName, mimeType, elementToDecrypt != null)
|
||||
|
||||
if (!cachedFiles.file.exists()) {
|
||||
val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: throw IllegalArgumentException("url is null")
|
||||
val resolvedUrl = contentUrlResolver.resolveForDownload(url, elementToDecrypt) ?: throw IllegalArgumentException("url is null")
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(resolvedUrl)
|
||||
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
|
||||
.build()
|
||||
val request = when (resolvedUrl) {
|
||||
is ContentUrlResolver.ResolvedMethod.GET -> {
|
||||
Request.Builder()
|
||||
.url(resolvedUrl.url)
|
||||
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
|
||||
.build()
|
||||
}
|
||||
|
||||
is ContentUrlResolver.ResolvedMethod.POST -> {
|
||||
Request.Builder()
|
||||
.url(resolvedUrl.url)
|
||||
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
|
||||
.post(resolvedUrl.jsonBody.toRequestBody("application/json".toMediaType()))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
val response = try {
|
||||
okHttpClient.newCall(request).execute()
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.cache.CacheService
|
|||
import org.matrix.android.sdk.api.session.call.CallSignalingService
|
||||
import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker
|
||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||
import org.matrix.android.sdk.api.session.contentscanner.ContentScannerService
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.events.EventService
|
||||
import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
|
||||
|
@ -124,6 +125,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||
private val accountService: Lazy<AccountService>,
|
||||
private val eventService: Lazy<EventService>,
|
||||
private val contentScannerService: Lazy<ContentScannerService>,
|
||||
private val identityService: IdentityService,
|
||||
private val integrationManagerService: IntegrationManagerService,
|
||||
private val thirdPartyService: Lazy<ThirdPartyService>,
|
||||
|
@ -174,8 +176,8 @@ internal class DefaultSession @Inject constructor(
|
|||
lifecycleObservers.forEach {
|
||||
it.onSessionStarted(this)
|
||||
}
|
||||
sessionListeners.dispatch { _, listener ->
|
||||
listener.onSessionStarted(this)
|
||||
dispatchTo(sessionListeners) { session, listener ->
|
||||
listener.onSessionStarted(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,8 +219,8 @@ internal class DefaultSession @Inject constructor(
|
|||
// timelineEventDecryptor.destroy()
|
||||
uiHandler.post {
|
||||
lifecycleObservers.forEach { it.onSessionStopped(this) }
|
||||
sessionListeners.dispatch { _, listener ->
|
||||
listener.onSessionStopped(this)
|
||||
dispatchTo(sessionListeners) { session, listener ->
|
||||
listener.onSessionStopped(session)
|
||||
}
|
||||
}
|
||||
cryptoService.get().close()
|
||||
|
@ -249,8 +251,8 @@ internal class DefaultSession @Inject constructor(
|
|||
lifecycleObservers.forEach {
|
||||
it.onClearCache(this)
|
||||
}
|
||||
sessionListeners.dispatch { _, listener ->
|
||||
listener.onClearCache(this)
|
||||
dispatchTo(sessionListeners) { session, listener ->
|
||||
listener.onClearCache(session)
|
||||
}
|
||||
}
|
||||
withContext(NonCancellable) {
|
||||
|
@ -260,8 +262,8 @@ internal class DefaultSession @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onGlobalError(globalError: GlobalError) {
|
||||
sessionListeners.dispatch { _, listener ->
|
||||
listener.onGlobalError(this, globalError)
|
||||
dispatchTo(sessionListeners) { session, listener ->
|
||||
listener.onGlobalError(session, globalError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,6 +277,8 @@ internal class DefaultSession @Inject constructor(
|
|||
|
||||
override fun cryptoService(): CryptoService = cryptoService.get()
|
||||
|
||||
override fun contentScannerService(): ContentScannerService = contentScannerService.get()
|
||||
|
||||
override fun identityService() = identityService
|
||||
|
||||
override fun fileService(): FileService = defaultFileService.get()
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.session.cache.CacheModule
|
|||
import org.matrix.android.sdk.internal.session.call.CallModule
|
||||
import org.matrix.android.sdk.internal.session.content.ContentModule
|
||||
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
|
||||
import org.matrix.android.sdk.internal.session.contentscanner.ContentScannerModule
|
||||
import org.matrix.android.sdk.internal.session.filter.FilterModule
|
||||
import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker
|
||||
import org.matrix.android.sdk.internal.session.group.GroupModule
|
||||
|
@ -94,6 +95,7 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
|
|||
AccountModule::class,
|
||||
FederationModule::class,
|
||||
CallModule::class,
|
||||
ContentScannerModule::class,
|
||||
SearchModule::class,
|
||||
ThirdPartyModule::class,
|
||||
SpaceModule::class,
|
||||
|
|
|
@ -18,15 +18,11 @@ package org.matrix.android.sdk.internal.session
|
|||
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.internal.SessionManager
|
||||
import org.matrix.android.sdk.internal.di.SessionId
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class SessionListeners @Inject constructor(
|
||||
@SessionId private val sessionId: String,
|
||||
private val sessionManager: SessionManager) {
|
||||
internal class SessionListeners @Inject constructor() {
|
||||
|
||||
private val listeners = mutableSetOf<Session.Listener>()
|
||||
|
||||
|
@ -42,18 +38,19 @@ internal class SessionListeners @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun dispatch(block: (Session, Session.Listener) -> Unit) {
|
||||
fun dispatch(session: Session, block: (Session, Session.Listener) -> Unit) {
|
||||
synchronized(listeners) {
|
||||
val session = getSession() ?: return Unit.also {
|
||||
Timber.w("You don't have any attached session")
|
||||
}
|
||||
listeners.forEach {
|
||||
tryOrNull { block(session, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSession(): Session? {
|
||||
return sessionManager.getSessionComponent(sessionId)?.session()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Session?.dispatchTo(sessionListeners: SessionListeners, block: (Session, Session.Listener) -> Unit) {
|
||||
if (this == null) {
|
||||
Timber.w("You don't have any attached session")
|
||||
return
|
||||
}
|
||||
sessionListeners.dispatch(this, block)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue