mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 02:45:53 +03:00
Merge tag 'v1.1.8' into sc
Change-Id: Ied7cd01e47a76e9d8f546ae1c2d6f10b083c4480 Conflicts: matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt
This commit is contained in:
commit
6c06462ec9
431 changed files with 12675 additions and 3441 deletions
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -5,6 +5,6 @@
|
|||
- [ ] Changes has been tested on an Android device or Android emulator with API 21
|
||||
- [ ] UI change has been tested on both light and dark themes
|
||||
- [ ] Pull request is based on the develop branch
|
||||
- [ ] Pull request updates [CHANGES.md](https://github.com/vector-im/element-android/blob/develop/CHANGES.md)
|
||||
- [ ] Pull request includes a new file under ./newsfragment. See https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#changelog
|
||||
- [ ] Pull request includes screenshots or videos if containing UI changes
|
||||
- [ ] Pull request includes a [sign off](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md#sign-off)
|
||||
|
|
59
.github/workflows/build.yml
vendored
Normal file
59
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
name: APK Build
|
||||
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
debug:
|
||||
name: Build debug APKs (${{ matrix.target }})
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref != 'refs/heads/main'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [ Gplay, Fdroid ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Assemble ${{ matrix.target }} debug apk
|
||||
run: ./gradlew assemble${{ matrix.target }}Debug --stacktrace
|
||||
- name: Upload ${{ matrix.target }} debug APKs
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: vector-${{ matrix.target }}-debug
|
||||
path: |
|
||||
vector/build/outputs/apk/*/debug/*.apk
|
||||
|
||||
release:
|
||||
name: Build unsigned GPlay APKs
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Assemble GPlay unsigned apk
|
||||
run: ./gradlew clean assembleGplayRelease --stacktrace
|
||||
- name: Upload Gplay unsigned APKs
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: vector-gplay-release-unsigned
|
||||
path: |
|
||||
vector/build/outputs/apk/*/release/*.apk
|
||||
|
||||
# TODO: add exodus checks
|
49
.github/workflows/integration.yml
vendored
Normal file
49
.github/workflows/integration.yml
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
name: Integration Test
|
||||
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Integration Tests (Synapse)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [21, 30]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Start synapse server
|
||||
run: |
|
||||
python3 -m venv .synapse
|
||||
source .synapse/bin/activate
|
||||
pip install synapse matrix-synapse
|
||||
curl -sL https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh --no-rate-limit \
|
||||
| sed s/127.0.0.1/0.0.0.0/g | bash
|
||||
- name: Run integration tests on API ${{ matrix.api-level }}
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
# script: ./gradlew -PallWarningsAsErrors=false vector:connectedAndroidTest matrix-sdk-android:connectedAndroidTest
|
||||
script: ./gradlew -PallWarningsAsErrors=false connectedCheck
|
74
.github/workflows/quality.yml
vendored
Normal file
74
.github/workflows/quality.yml
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
name: Code Quality Checks
|
||||
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Project Check Suite
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run code quality check suite
|
||||
run: ./tools/check/check_code_quality.sh
|
||||
|
||||
klint:
|
||||
name: Kotlin Linter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run klint
|
||||
run: |
|
||||
curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.36.0/ktlint && chmod a+x ktlint
|
||||
./ktlint --android --experimental -v
|
||||
|
||||
android-lint:
|
||||
name: Android Linter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Lint analysis of the SDK
|
||||
run: ./gradlew clean :matrix-sdk-android:lintRelease --stacktrace
|
||||
- name: Upload reports
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linting-report-android-sdk
|
||||
path: matrix-sdk-android/build/reports/*.*
|
||||
|
||||
apk-lint:
|
||||
name: Lint APK (${{ matrix.target }})
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref != 'refs/heads/main'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [ Gplay, Fdroid ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Lint ${{ matrix.target }} release
|
||||
run: ./gradlew clean lint${{ matrix.target }}Release --stacktrace
|
||||
- name: Upload ${{ matrix.target }} linting report
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: release-debug-linting-report-${{ matrix.target }}
|
||||
path: |
|
||||
vector/build/reports/*.*
|
23
.github/workflows/tests.yml
vendored
Normal file
23
.github/workflows/tests.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: Test
|
||||
|
||||
on:
|
||||
pull_request: {}
|
||||
push:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Run Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Run unit tests
|
||||
run: ./gradlew clean test --stacktrace -PallWarningsAsErrors=false
|
46
.travis.yml
46
.travis.yml
|
@ -1,4 +1,4 @@
|
|||
# FTR: Configuration on https://travis-ci.org/vector-im/riotX-android/settings
|
||||
# FTR: Configuration on https://travis-ci.org/github/vector-im/element-android/settings
|
||||
#
|
||||
# - Build only if .travis.yml is present -> On
|
||||
# - Limit concurrent jobs -> Off
|
||||
|
@ -8,53 +8,11 @@
|
|||
# - Auto cancel branch builds -> On
|
||||
# - Auto cancel pull request builds -> On
|
||||
|
||||
language: android
|
||||
jdk: oraclejdk8
|
||||
sudo: false
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
android:
|
||||
components:
|
||||
# Uncomment the lines below if you want to
|
||||
# use the latest revision of Android SDK Tools
|
||||
- tools
|
||||
- platform-tools
|
||||
|
||||
# The BuildTools version used by your project
|
||||
- build-tools-29.0.3
|
||||
|
||||
# The SDK version used to compile your project
|
||||
- android-29
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
- $HOME/.android/build-cache
|
||||
|
||||
# Build with the development SDK
|
||||
before_script:
|
||||
# Not necessary for the moment
|
||||
# - /bin/sh ./set_debug_env.sh
|
||||
|
||||
# Just build the project for now
|
||||
# Just run a simple script here
|
||||
script:
|
||||
# Build app (assembleGplayRelease assembleFdroidRelease)
|
||||
# Build Android test (assembleAndroidTest) (disabled for now)
|
||||
# Code quality (lintGplayRelease lintFdroidRelease)
|
||||
# Split into two steps because if a task contain Fdroid, PlayService will be disabled
|
||||
# Done by Buildkite now: - ./gradlew clean assembleGplayRelease lintGplayRelease --stacktrace
|
||||
# Done by Buildkite now: - ./gradlew clean assembleFdroidRelease lintFdroidRelease --stacktrace
|
||||
# Run unitary test (Disable for now, see https://travis-ci.org/vector-im/riot-android/builds/502504370)
|
||||
# - ./gradlew testGplayReleaseUnitTest --stacktrace
|
||||
# Other code quality check
|
||||
# Done by Buildkite now: - ./tools/check/check_code_quality.sh
|
||||
- ./tools/travis/check_pr.sh
|
||||
# Check that indonesians file are identical. Due to Android issue, the resource folder must be value-in/, and Weblate export data into value-id/.
|
||||
# Done by Buildkite now: - diff ./vector/src/main/res/values-id/strings.xml ./vector/src/main/res/values-in/strings.xml
|
||||
|
|
64
CHANGES.md
64
CHANGES.md
|
@ -1,3 +1,30 @@
|
|||
Changes in Element 1.1.8 (2021-05-25)
|
||||
===================================================
|
||||
|
||||
Improvements 🙌:
|
||||
- Support Jitsi authentication (#3379)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Space Invite by link not always displayed for public space (#3345)
|
||||
- Wrong copy in share space bottom sheet (#3346)
|
||||
- Fix a problem with database migration on nightly builds (#3335)
|
||||
- Implement a workaround to render <del> and <u> in the timeline (#1817)
|
||||
- Make sure the SDK can retrieve the secret storage if the system is upgraded (#3304)
|
||||
- Spaces | Explore room list: the RoomId is displayed instead of name (#3371)
|
||||
- Spaces | Personal spaces add DM - Web Parity (#3271)
|
||||
- Spaces | Improve 'Leave Space' UX/UI (#3359)
|
||||
- Don't create private spaces with encryption enabled (#3363)
|
||||
- #+ button on lower right when looking at an empty space goes to an empty 'Explore rooms' (#3327)
|
||||
|
||||
Build 🧱:
|
||||
- Compile with Kotlin 1.5.10.
|
||||
- Upgrade some dependencies: gradle wrapper, third party lib, etc.
|
||||
- Sign APK with build tools 30.0.3
|
||||
|
||||
Other changes:
|
||||
- Add documentation on LoginWizard and RegistrationWizard (#3303)
|
||||
- Setup towncrier tool (#3293)
|
||||
|
||||
Changes in Element 1.1.7 (2021-05-12)
|
||||
===================================================
|
||||
|
||||
|
@ -67,7 +94,7 @@ Changes in Element 1.1.4 (2021-04-09)
|
|||
|
||||
Improvements 🙌:
|
||||
- Split network request `/keys/query` into smaller requests (250 users max) (#2925)
|
||||
- Crypto improvement | Bulk send NO_OLM withheld code
|
||||
- Crypto improvement | Bulk send NO_OLM withheld code
|
||||
- Display the room shield in all room setting screens
|
||||
- Improve message with Emoji only detection (#3017)
|
||||
- Picture preview when replying. Also add the image preview in the message detail bottomsheet (#2916)
|
||||
|
@ -626,7 +653,7 @@ Improvements 🙌:
|
|||
- Sending events is now retried only 3 times, so we avoid blocking the sending queue too long.
|
||||
- Display warning when fail to send events in room list
|
||||
- Improve UI of edit role action in member profile
|
||||
- Moderation | New screen to display list of banned users in room settings, with unban action
|
||||
- Moderation | New screen to display list of banned users in room settings, with unban action
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix theme issue on Room directory screen (#1613)
|
||||
|
@ -1346,36 +1373,3 @@ Changes in RiotX 0.1.0 (2019-07-11)
|
|||
First release!
|
||||
|
||||
Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-android-b17952e8f771
|
||||
|
||||
|
||||
=======================================================
|
||||
+ TEMPLATE WHEN PREPARING A NEW RELEASE +
|
||||
=======================================================
|
||||
|
||||
|
||||
Changes in Element 1.1.X (2021-XX-XX)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
-
|
||||
|
||||
Improvements 🙌:
|
||||
-
|
||||
|
||||
Bugfix 🐛:
|
||||
-
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
|
||||
SDK API changes ⚠️:
|
||||
-
|
||||
|
||||
Build 🧱:
|
||||
-
|
||||
|
||||
Test:
|
||||
-
|
||||
|
||||
Other changes:
|
||||
-
|
||||
|
|
|
@ -51,9 +51,21 @@ If an issue does not exist yet, it may be relevant to open a new issue and let u
|
|||
|
||||
This project is full Kotlin. Please do not write Java classes.
|
||||
|
||||
### CHANGES.md
|
||||
### Changelog
|
||||
|
||||
Please add a line to the top of the file `CHANGES.md` describing your change.
|
||||
Please create at least one file under ./newsfragment containing details about your change. Towncrier will be used when preparing the release.
|
||||
|
||||
Towncrier says to use the PR number for the filename, but the issue number is also fine.
|
||||
|
||||
Supported filename extensions are:
|
||||
|
||||
- ``.feature``: Signifying a new feature in Element Android or in the Matrix SDK.
|
||||
- ``.bugfix``: Signifying a bug fix.
|
||||
- ``.doc``: Signifying a documentation improvement.
|
||||
- ``.removal``: Signifying a deprecation or removal of public API. Can be used to notifying about API change in the Matrix SDK
|
||||
- ``.misc``: A ticket has been closed, but it is not of interest to users. Note that in this case, the content of the file will not be output, but just the issue/PR number.
|
||||
|
||||
See https://github.com/twisted/towncrier#news-fragments if you need more details.
|
||||
|
||||
### Code quality
|
||||
|
||||
|
|
|
@ -47,14 +47,14 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.1.4'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
|
|
|
@ -33,6 +33,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.transition.TransitionManager
|
||||
|
@ -124,9 +125,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
|||
scaleDetector = createScaleGestureDetector()
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(views.rootContainer) { _, insets ->
|
||||
overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom)
|
||||
topInset = insets.systemWindowInsetTop
|
||||
bottomInset = insets.systemWindowInsetBottom
|
||||
val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
overlayView?.updatePadding(top = systemBarsInsets.top, bottom = systemBarsInsets.bottom)
|
||||
topInset = systemBarsInsets.top
|
||||
bottomInset = systemBarsInsets.bottom
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
|
10
build.gradle
10
build.gradle
|
@ -2,8 +2,8 @@
|
|||
|
||||
buildscript {
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
ext.kotlin_version = '1.4.32'
|
||||
ext.kotlin_coroutines_version = "1.4.2"
|
||||
ext.kotlin_version = '1.5.10'
|
||||
ext.kotlin_coroutines_version = "1.5.0"
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
@ -12,10 +12,10 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
classpath 'com.google.gms:google-services:4.3.5'
|
||||
classpath 'com.android.tools.build:gradle:4.2.1'
|
||||
classpath 'com.google.gms:google-services:4.3.8'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1'
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.2.0'
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
|
||||
classpath "com.likethesalad.android:string-reference:1.2.2"
|
||||
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
Element ist mehr als ein sicherer Messenger. Es ist ein produktives Kolaborationsapp für das Team und eignet sich ideal für den Gruppenchat beim Arbeiten von zuhause aus. Mit eingebauter Ende-zu-Ende-Verschlüsselung ermöglicht Element umfangreiche und sichere Videokonferenzen, das Teilen von Dokumenten/Dateien und Videoanrufe.
|
||||
Element ist einerseits ein sicherer Messenger, andererseits ideal geeignet für die produktive Zusammenarbeit mit dem Team im Homeoffice. Mit eingebauter Ende-zu-Ende-Verschlüsselung ermöglicht Element umfangreiche und sichere Videokonferenzen, das Teilen von Dateien sowie Sprachanrufe.
|
||||
|
||||
<b>Element enthält folgende Funktionen:</b>
|
||||
- Fortgeschrittene Werkzeuge für die Online-Kommunikation
|
||||
- Vollverschlüsselte Nachrichten um eine sichere Kommunikation innerhalb und außerhalb des Unternehmens zu ermöglichen
|
||||
- Dezentralisierte Chats basierend auf das quelloffene Matrix-Framework
|
||||
- Sichere und kontrollierte Dateienfreigabe durch verschlüsselte Daten beim verwalten von Projekten
|
||||
- Videochats über VoIP und Bildschirmübertragung
|
||||
- Einfache Einbindungen mit Ihren favorisierten Online-Kolaborationswerkzeugen, Projektverwaltungswerkzeugen, VoIP-Diensten und andere Kommunikationsapps für Ihren Team
|
||||
<b>Element bietet folgende Funktionen:</b>
|
||||
- Fortschrittliche Werkzeuge für die Online-Kommunikation
|
||||
- Vollständig verschlüsselte Nachrichten, um eine sichere Kommunikation innerhalb und außerhalb von Unternehmen zu ermöglichen
|
||||
- Dezentralisierte Chats, basierend auf dem quelloffenen Matrix-Framework
|
||||
- Sichere und kontrollierte Dateifreigabe durch verschlüsselte Daten beim Verwalten von Projekten
|
||||
- Videochats mit VoIP und Bildschirmübertragung
|
||||
- Einfache Einbindung in Ihre bevorzugten Online-Kollaborations- und Projektverwaltungswerkzeuge, VoIP-Dienste und andere Kommunikationsapps für Ihr Team
|
||||
|
||||
Element unterscheidet sich deutlich von anderen Kommunikations- und Kollaborationsapps. Es läuft auf Matrix, ein offenes Netzwerk für eine sichere und dezentralisierte Kommunikation. Es erlaubt den Nutzern ihre eigenen Matrix-Dienste zu betreiben und gibt ihnen damit die vollständige Kontrolle und Besitz über ihre eigenen Daten und Nachrichten.
|
||||
Element unterscheidet sich grundlegend von anderen Kommunikations- und Kollaborationsapps. Es läuft auf Matrix, einem offenen Netzwerk für sichere und dezentralisierte Kommunikation. Es erlaubt Nutzern ihre eigene Infrastruktur zu betreiben und gibt ihnen damit vollständige Kontrolle und Besitz über ihre eigenen Daten und Nachrichten.
|
||||
|
||||
<b>Privatsphäre/Datenschutz und verschlüsselte Kommunikation</b>
|
||||
Element schützt Ihnen vor unerwünschte Werbung, das Datenschürfen und geschlossene unentkommbare Dienste. Auch schützt es all Ihre Daten, Video und Sprachkommunikation unter vier Augen durch Ende-zu-Ende-Verschlüsselung und das Quersignieren von Geräten zur Verifizierung.
|
||||
Element schützt Sie vor unerwünschter Werbung, dem Datenschürfen und abgeschlossenen Plattformen. Auch schützt es all Ihre Daten, Ihre Video- und Sprachkommunikation unter vier Augen, durch Ende-zu-Ende-Verschlüsselung und durch das Quersignieren von Geräten zur Verifizierung.
|
||||
|
||||
Element gibt Ihnen die Kontrolle über Ihre Privatsphäre, während es Ihnen ermöglicht mit jeden auf dem Matrix-Netzwerk oder andere geschäftliche Kollaborationswerkzeuge durch das Einbinden von Apps wie Slack sicher zu kommunizieren.
|
||||
Element gibt Ihnen die Kontrolle über Ihre Privatsphäre und ermöglicht es Ihnen zugleich, mit jedem im Matrix-Netzwerk sicher zu kommunizieren - oder auch auf anderen geschäftlichen Kollaborationswerkzeugen, zum Beispiel durch das Einbinden von Apps wie Slack.
|
||||
|
||||
<b>Element kann man selber betreiben</b>
|
||||
Um mehr Kontrolle über Ihre sensiblen Daten und Konversationen zu ermöglichen, kann man Element selbst betreiben oder Sie wählen irgendeinen Matrix-basierten Dienst - der standard für quelloffene, dezentralisierte Kommunikation. Element gibt Ihnen Privatsphäre, Sicherheitskonformität und die Flexibilität zum Integrieren.
|
||||
Um mehr Kontrolle über Ihre sensiblen Daten und Konversationen zu ermöglichen, kann man Element selbst betreiben, oder Sie wählen irgendeinen Matrix-basierten Dienst - der Standard für quelloffene, dezentralisierte Kommunikation. Element gibt Ihnen Privatsphäre, Sicherheitskonformität und Flexibilität für Integrationen.
|
||||
|
||||
<b>Besitzen Sie Ihre Daten</b>
|
||||
Sie entscheiden wo Sie Ihre Daten und Nachrichten aufbewahren, ohne das Risiko des Datenschürfens oder des Zugriffes Dritter.
|
||||
Sie entscheiden, wo Sie Ihre Daten und Nachrichten aufbewahren - ohne das Datenschürfen oder den Zugriff Dritter zu riskieren.
|
||||
|
||||
Element gibt Ihnen die Kontrolle durch verschiedene Wege:
|
||||
1. Kostenlos auf dem öffentlichen matrix.org Server registrieren, der von den Matrix-Entwicklern gehostet wird, oder wähle aus Tausenden von öffentlichen Servern, die von Freiwilligen gehostet werden
|
||||
2. Einen Konto auf einem eigenen Server in der eigenen IT-Infrastruktur betreiben
|
||||
3. Einen Konto auf einem benutzerdefinierten Server erstellen, zum Beispiel durch ein Abonnement bei Element Matrix Services (kurz EMS)
|
||||
Element gibt Ihnen auf verschiedene Arten die Kontrolle:
|
||||
1. Kostenlos auf dem öffentlichen matrix.org-Server registrieren, der von den Matrix-Entwicklern gehostet wird, oder wählen Sie aus Tausenden von öffentlichen Servern, die von Freiwilligen betrieben werden
|
||||
2. Ein Konto auf einem eigenen Server in der eigenen IT-Infrastruktur betreiben
|
||||
3. Einen Konto auf einem maßgeschneiderten Server erstellen, zum Beispiel durch ein Abonnement der Element Matrix Services (kurz EMS)
|
||||
|
||||
<b>Offene Kommunikation und Zusammenarbeit</b>
|
||||
Sie können mit jeden auf dem Matrix-Netzwerk chatten, egal ob sie Element, eine Matrix-App oder sogar eine andere Kommunikationsapp nutzen.
|
||||
Sie können mit jedem im Matrix-Netzwerk chatten, egal ob ihr Kontakt Element, eine andere Matrix-App oder sogar eine völlig andere Anwendung nutzt.
|
||||
|
||||
<b>Super sicher</b>
|
||||
Reale Ende-zu-Ende-Verschlüsselung (nur die Personen in der Konversation können die Nachricht entschüsseln) und Quersignierung von Geräten zur Verifizierung.
|
||||
Echte Ende-zu-Ende-Verschlüsselung (nur die Personen in der Unterhaltung können die Nachrichten entschüsseln), sowie die Quersignierung von Geräten zur Verifizierung.
|
||||
|
||||
<b>Vollständige Kommunikation und Integration</b>
|
||||
Kurznachrichten, Sprach- und Videoanrufe, kontrollierte Dateifreigaben, Bildschirmübertragungen und eine ganze Reihe an Integrationen, Bots and Widgets. Schaffe Räume, Gemeinschaften, bleibe auf dem Laufenden und erledige Sachen.
|
||||
Kurznachrichten, Sprach- und Videoanrufe, Dateifreigaben, Bildschirmübertragungen und eine ganze Reihe an Integrationen, Bots and Widgets. Schaffen Sie Räume, Communities, bleiben Sie auf dem Laufenden und erledigen Sie Sachen.
|
||||
|
||||
<b>Das Stehengelassene später wieder aufgreifen</b>
|
||||
Bleibe auf dem Laufenden, egal wo Sie sind, mit vollständig synchronisierter Nachrichtenverlauf quer über all Ihrer Geräte und im Netz auf https://app.element.io
|
||||
<b>Da Weitermachen, wo Sie aufgehört haben</b>
|
||||
Bleiben Sie in Kontakt, egal wo Sie sind, mit vollständig synchronisiertem Nachrichtenverlauf quer über all Ihre Geräte und im Netz auf https://app.element.io
|
||||
|
|
2
fastlane/metadata/android/en-US/changelogs/40101080.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40101080.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: improvement for Spaces.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.8
|
|
@ -1,30 +1,39 @@
|
|||
Element on uudenlainen viestinsovellus, joka:
|
||||
Element on turvallinen pikaviesti- ja tiimityösovellus joka sopii mainiosti ryhmäkeskusteluihin etätöissä. Sovellus käyttää päästä päähän -salausta ja tarjoaa videoneuvottelun, tiedostojen jakamisen ja äänipuhelut.
|
||||
|
||||
1. Antaa sinun päättää yksityisyydestäsi
|
||||
2. Antaa sinun kommunikoida kenen tahansa kanssa Matrix-verkossa ja jopa sen ulkopuolella siltaamalla sovelluksiin, kuten Slack
|
||||
3. Suojaa sinua mainonnalta, tietojen keräämiseltä ja suljetuilta alustoilta
|
||||
4. Suojaa sinut päästä päähän -salauksella sekä ristiin varmentamisella muiden todentamiseksi
|
||||
<b>Elementin ominaisuuksia:</b>
|
||||
- Edistyneet viestintätyökalut
|
||||
- Turvallisempaa yritysviestintää täysin salatuilla viesteillä, myös etätyöntekijöille
|
||||
- Hajautettu keskustelu, joka perustuu avoimen lähdekoodin Matrix-teknologiaan
|
||||
- Turvallinen tiedostojen jakaminen datan salauksella
|
||||
- Videoneuvottelut VoIP:lla ja näytön jakamisella
|
||||
- Helppo integraatio tiimityövälineisiin, projektinhallintatyökaluihin, VoIP-palveluihin ja muihin ryhmäviestimiin
|
||||
|
||||
Element eroaa täysin muista viestintäsovelluksista, koska se on hajautettu ja avointa lähdekoodia.
|
||||
Element on täysin erilainen kuin muut viestintä- ja yhteistyösovellukset. Se toimii Matrixilla, avoimella turvallisen ja hajautetun viestinnän verkostolla. Palvelimen ylläpitäminen itse on mahdollista, jos haluaa tietonsa ja viestinsä täysin hallintaansa.
|
||||
|
||||
Element antaa sinun isännöidä itse - tai valita palveluntarjoajan - jotta sinulla on yksityisyys ja voit hallita tietojasi sekä keskustelujasi. Se antaa sinulle pääsyn avoimeen verkkoon, joten et jää juttelemaan vain toisten Elementin käyttäjien kanssa. Se on myös hyvin turvallinen.
|
||||
<b>Yksityisyys ja salattu viestintä</b>
|
||||
Element protects you from unwanted ads, data mining and walled gardens. It also secures all your data, one-to-one video and voice communication through end-to-end encryption and cross-signed device verification.
|
||||
|
||||
Element pystyy tekemään kaiken tämän, koska se toimii Matrixilla - avoimella, hajautetun viestinnän standardilla.
|
||||
Element gives you control over your privacy while allowing you to communicate securely with anyone on the Matrix network, or other business collaboration tools by integrating with apps such as Slack.
|
||||
|
||||
Element antaa sinulle päätösvallan antamalla sinun valita, kuka isännöi keskustelujasi. Element-sovelluksessa voit valita isännän eri tavoin:
|
||||
<b>Voit ylläpitää omaa palvelinta</b>
|
||||
To allow more control of your sensitive data and conversations, Element can be self-hosted or you can choose any Matrix-based host - the standard for open source, decentralized communication. Element gives you privacy, security compliance and integration flexibility.
|
||||
|
||||
1. Hanki ilmainen tili Matrix-kehittäjien ylläpitämällä matrix.org-palvelimella tai valitse tuhansista vapaaehtoisten ylläpitämistä julkisista palvelimista.
|
||||
2. Isännöi tiliäsi itse ylläpitämällä palvelinta omalla laitteellasi
|
||||
3. Luo tili sinua varten tehdyllä palvelimella tilaamalla Element Matrix Services -palvelu
|
||||
<b>Hallitse omia tietojasi</b>
|
||||
You decide where to keep your data and messages. Without the risk of data mining or access from third parties.
|
||||
|
||||
<b>Miksi valita Element?</b>
|
||||
Element puts you in control in different ways:
|
||||
1. Get a free account on the matrix.org public server hosted by the Matrix developers, or choose from thousands of public servers hosted by volunteers
|
||||
2. Self-host your account by running a server on your own IT infrastructure
|
||||
3. Sign up for an account on a custom server by simply subscribing to the Element Matrix Services hosting platform
|
||||
|
||||
<b>OMAT TIEDOT</b>: Sinä päätät, missä tietosi ja viestisi säilytetään. Sinä määräät, ei jokin jättiyhtiö, joka tutkii tietojasi tai antaa niitä kolmansille osapuolille.
|
||||
<b>Avointa viestintää ja yhteistyötä</b>
|
||||
Voit keskustella kenen tahansa kanssa Matrix-verkossa, riippumatta siitä käyttääkö tämä Elementiä, jotain muuta Matrix-sovellusta tai jopa muuta viestintäsovellusta.
|
||||
|
||||
<b>AVOINTA VIESTINTÄÄ JA YHTEISTYÖTÄ</b>: Voit keskustella kaikkien muiden Matrix-verkon käyttäjien kanssa, riippumatta siitä käyttävätkö he Elementiä tai muuta Matrix-sovellusta, ja vaikka he käyttäisivät eri viestijärjestelmiä, kuten Slack, IRC tai XMPP.
|
||||
<b>Todella turvallinen</b>
|
||||
Real end-to-end encryption (only those in the conversation can decrypt messages), and cross-signed device verification.
|
||||
|
||||
<b>ERITTÄIN TURVALLINEN</b>: Vahva päästä päähän -salaus (vain keskustelussa olevat voivat purkaa viestien salauksen), ja ristiin varmentaminen keskustelun osallistujien laitteiden tarkistamiseksi.
|
||||
<b>Kattavaa viestintää ja integraatioita</b>
|
||||
Viestit, ääni- ja videopuhelut, tiedostojen jakaminen, näytön jakaminen ja koko joukko integraatioita, botteja ja sovelmia. Luo huoneita ja yhteisöjä, pidä yhteyttä ja hoida asiasi.
|
||||
|
||||
<b>KATTAVAA VIESTINTÄÄ</b>: Viestit, ääni- ja videopuhelut, tiedostojen jakaminen, näytön jakaminen ja koko joukko integraatioita, botteja ja sovelmia. Rakenna huoneita ja yhteisöjä, pidä yhteyttä ja hoida asiasi.
|
||||
|
||||
<b>MISSÄ TAHANSA OLETKIN</b>: Pidä yhteyttä missä tahansa, täysin synkronoidun viestihistorian kautta kaikilla laitteillasi ja verkossa osoitteessa https://app.element.io.
|
||||
<b>Jatka siitä mihin jäit</b>
|
||||
Stay in touch wherever you are with fully synchronised message history across all your devices and on the web at https://app.element.io
|
||||
|
|
|
@ -1 +1 @@
|
|||
Turvallista, hajautettua keskustelua ja VoIP-puheluita. Pidä tietosi turvassa.
|
||||
Ryhmäviestin - salattua viestintää, ryhmäkeskusteluja ja videopuheluita
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (aiemmin Riot.im)
|
||||
Element - Turvallinen viestin
|
||||
|
|
|
@ -1 +1 @@
|
|||
Messagerie de groupes - messages chiffrés, groupés et appels vidéos
|
||||
Messagerie de groupe - messages chiffrés, groupes et appels vidéos
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
Principais mudanças nessa versão: correções de erros!
|
||||
Registro de todas as alterações: https://github.com/vector-im/element-android/releases/tag/v1.0.17
|
||||
Principais alterações nesta versão: Correções de bugs!
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.0.17
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
Principais mudanças nesta versão: Melhoria de VoIP (chamadas de áudio e vídeo em conversas) e correção de erros!
|
||||
Registro de alterações completo: https://github.com/vector-im/element-android/releases/tag/v1.1.0
|
||||
Principais alterações nesta versão: melhoramento de VoIP (chamadas de áudio e vídeo em DM) e correções de bugs!
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.0
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
Principais mudanças nesta versão: melhoria de desempenho e correção de erros!
|
||||
Registro de alterações completo: https://github.com/vector-im/element-android/releases/tag/v1.1.1
|
||||
Principais alterações nesta versão: melhoramento de performance e correções de bugs!
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.1
|
||||
|
|
2
fastlane/metadata/android/pt-BR/changelogs/40101020.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101020.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais alterações nesta versão: melhoramento de performance e correções de bugs!
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.2
|
2
fastlane/metadata/android/pt-BR/changelogs/40101030.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101030.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais alterações nesta versão: melhoramento de performance e correções de bugs!
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.3
|
2
fastlane/metadata/android/pt-BR/changelogs/40101040.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101040.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais alterações nesta versão: melhora de performance e correções de bugs!
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.4
|
2
fastlane/metadata/android/pt-BR/changelogs/40101050.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais alterações nesta versão: correções quentes para 1.1.4
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.5
|
2
fastlane/metadata/android/pt-BR/changelogs/40101060.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais alterações nesta versão: correções quentes para 1.1.5
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.6
|
|
@ -1,30 +1,39 @@
|
|||
Element é um novo tipo de aplicativo de mensagens e colaboração que:
|
||||
Element é tanto um mensageiro seguro como um app de colaboração de time de produtividade que é ideal para chats de grupo enquanto se trabalha remotamente. Este app de chat usa encriptação ponta-a-ponta para prover conferência de vídeo, compartilhamento de arquivo e chamadas de voz poderasos.
|
||||
|
||||
1. Coloca você no controle de sua privacidade;
|
||||
2. Permite que você se comunique com qualquer pessoa na rede Matrix e, através de integrações, outros aplicativos como o Slack;
|
||||
3. Protege você de anúncios, mineração de dados e de ecossistemas fechados;
|
||||
4. Faz uso da criptografia de ponta a ponta, com autoverificação para confirmar outras pessoas.
|
||||
<b>As funções de Element incluem:</b>
|
||||
- Ferramentas de comunicação online avançadas
|
||||
- Mensagens completamente encriptadas para permitir comunicação de corporação mais segura, até para pessoas trabalhando remotamente
|
||||
- Chat descentralizado baseado na framework open source Matrix
|
||||
- Compartilhamento de arquivo seguramente com dados encriptados enquanto se gerencia projetos
|
||||
- Chats de vídeo com Voz sobre IP e compartilhamento de tela
|
||||
- Integração fácil com suas ferramentas de colaboração online favoritas, ferramentas de gerenciamento de projetos, serviços de VoIP e outros apps de mensageria de time
|
||||
|
||||
Element é completamente diferente de outros aplicativos de mensagem e colaboração porque é descentralizado e código aberto.
|
||||
Element é completamente diferente de outros apps de mensageria e colaboração. Ele opera em Matrix, uma rede aberta para mensageria segura e comunicação descentralizada. Ele permite auto-hospedagem para dar a usuárias(os) máxima propriedade e controle de seus dados e suas mensagens.
|
||||
|
||||
É possível hospedar um servidor ou escolher um servidor hospedeiro, para que você tenha privacidade, controle e que você seja o dono de seus dados e conversas. Com o Element, você possui acesso a uma rede aberta; então você não está preso falando somente com outros usuários no Element. O Element também é muito seguro.
|
||||
<b>Privacidade e mensageria encriptada</b>
|
||||
Element protege você de ads não-desejados, minagem de dados e jardins murados. Ele também assegura todos os seus dados, vídeo um-a-um e comunicação de voz através de encriptação ponta-a-ponta e verificação de dispositivo assinada cruzado.
|
||||
|
||||
Element é capaz de fazer tudo isso porque ele opera no Matrix, o padrão para comunicação aberta e descentralizada.
|
||||
Element dá a você controle sobre sua privacidade enquanto permite a você se comunicar seguramente com qualquer pessoa na rede Matrix, ou outras ferramentas de colaboração ao se integrar com apps tais como Slack.
|
||||
|
||||
Element coloca você no controle ao permitir quem hospeda suas conversas. Neste aplicativo, você pode escolher hospedar de maneiras diferentes:
|
||||
<b>Element pode ser auto-hospedado</b>
|
||||
Para permitir mais controle de seus dados e conversas sensíveis, Element pode ser auto-hospedado ou você pode escolher qualquer host baseado em Matrix - o standard para comunicação open source e descentralizada. Element dá a você privacidade, conformidade de segurança e flexibilidade de integração.
|
||||
|
||||
1. Crie uma conta gratuita no servidor público matrix.org;
|
||||
2. Hospede sua conta no seu servidor;
|
||||
3. Entre em uma conta em um servidor customizado ao simplesmente se inscrever na plataforma de hospedagem Serviços Matrix Element.
|
||||
<b>Tenha posse de seus dados</b>
|
||||
Você decidade onde mannter seus dados e mensagens. Sem o risco de minagem de dados ou acesso de terceiros.
|
||||
|
||||
<b>Por que escolher o Element?</b>
|
||||
Element põe você em controle de diferentes maneiras:
|
||||
1. Tenha uma conta grátis no servidor público matrix.org hospedado pelos desenvolvedores Matrix, ou escolha de milhares de servidores públicos hospedados por pessoas se voluntariando
|
||||
2. Auto-hospede sua conta ao rodar um servidor em sua própria infraestrutura de TI
|
||||
3. Registre-se para uma conta num servidor personalizado ao simplesmente assinar a plataforma de hospedagem Element Matrix Services
|
||||
|
||||
<b>SEJA O DONO DE SEUS DADOS</b>: você decide onde manter seus dados e mensagens. Você é o dono deles e também os controla, não alguma mega corporação que minera os seus dados ou compartilha eles com terceiros.
|
||||
<b>Mensageria e colaboração abertos</b>
|
||||
Você pode fazer chat com qualquer pessoa na rede Matrix, caso ela esteja usando Element, um outro app de Matrix ou mesmo se ela estiver usando um app de mensagem diferente.
|
||||
|
||||
<b>COLABORAÇÃO E MENSAGENS ABERTAS</b>: você pode falar com qualquer outra pessoa na rede Matrix, não importa se ela está usando o Element ou algum outro aplicativo Matrix, ou até mesmo se ela está utilizando um sistema de mensagens diferente como o Slack, IRC ou XMPP.
|
||||
<b>Super seguro</b>
|
||||
Encriptação ponta-a-ponta real (somente aquelas/es na conversa podem decriptar mensagens), e verificação de dispositivo assinada cuzado.
|
||||
|
||||
<b>SUPER-SEGURO</b>: criptografia de ponta a ponta verdadeira (apenas aqueles na conversa podem descriptografar mensagens), e assinatura cruzada para verificar os dispositivos de participantes de conversas.
|
||||
<b>Comunicação e integração completas</b>
|
||||
Messageria, chamas de voz e vídeo, compartilhamento de arquivo, compartilhamento de tela e um monte de integrações, bots e widgets. Construa salas, comunidades, fique em contato e tenha as coisas feitas.
|
||||
|
||||
<b>COMUNICAÇÃO COMPLETA</b>: mensagens, chamadas de voz, chamadas de vídeo, compartilhamento de arquivos, compartilhamento de tela e um monte de integrações, robôs e widgets. Construa salas, comunidades, mantenha contato e faça seus projetos.
|
||||
|
||||
<b>NÃO IMPORTA ONDE VOCÊ ESTEJA</b>: mantenha contato não importa onde você esteja com o histórico sincronizado de mensagens em todos os seus dispositivos e no navegador em https://app.element.io.
|
||||
<b>Continue de onde você parou</b>
|
||||
Fique em contato onde quer que você esteja com histórico de mensagem completamente sincronizado por todos os seus dispositivos e na web em https://app.element.io
|
||||
|
|
|
@ -1 +1 @@
|
|||
Conversas e chamadas seguras e descentralizadas. Mantenha seus dados protegidos.
|
||||
Mensageiro de grupo - mensagens encriptadas, chat de grupo e chamadas de vídeo
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (o novo Riot.im)
|
||||
Element - Mensageiro Seguro
|
||||
|
|
1
fastlane/metadata/android/ro/short_description.txt
Normal file
1
fastlane/metadata/android/ro/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Mesagerie de grup - mesaje criptate, comunicare de grup și apeluri video
|
1
fastlane/metadata/android/ro/title.txt
Normal file
1
fastlane/metadata/android/ro/title.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Element - Mesagerie securizată
|
2
fastlane/metadata/android/uk/changelogs/40101040.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40101040.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни в цій версії: поліпшення швидкодії та виправлення помилок!
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.4
|
2
fastlane/metadata/android/uk/changelogs/40101050.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40101050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни в цій версії: виправлення для 1.1.4
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.5
|
2
fastlane/metadata/android/uk/changelogs/40101060.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40101060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни в цій версії: виправлення для 1.1.5
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.6
|
|
@ -1 +1 @@
|
|||
Захищене децентралізоване листування та дзвінки. Тримайте ваші дані в безпеці.
|
||||
Груповий месенджер — зашифровані повідомлення, групові бесіди та відеовиклики
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (раніше Riot.im)
|
||||
Element — Безпечний месенджер
|
||||
|
|
|
@ -1 +1 @@
|
|||
Ứng dụng chat và gọi phân tán bảo mật. Bảo vệ dữ liệu của bạn khỏi bên thứ ba.
|
||||
Nhắn tin nhóm - tin nhắn được mã hoá, cuộc trò chuyện nhóm và cuộc gọi video
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (trước là Riot.im)
|
||||
Element - Nhắn tin bảo mật
|
||||
|
|
|
@ -1 +1 @@
|
|||
安全、去中心化的聊天与 VoIP 通话。保护您的数据不被第三方窃取。
|
||||
群组消息应用-加密的消息传递、群组聊天和视频通话
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element(曾为 Riot.im)
|
||||
Element - 安全消息应用
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=81003f83b0056d20eedf48cddd4f52a9813163d4ba185bcf8abd34b8eeea4cbd
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
|
||||
distributionSha256Sum=13bf8d3cf8eeeb5770d19741a59bde9bd966dd78d17f1bbad787a05ef19d1c2d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -35,7 +35,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlin_coroutines_version"
|
||||
|
|
|
@ -9,7 +9,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "io.realm:realm-gradle-plugin:10.4.0"
|
||||
classpath "io.realm:realm-gradle-plugin:10.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ dependencies {
|
|||
def lifecycle_version = '2.2.0'
|
||||
def arch_version = '2.1.0'
|
||||
def markwon_version = '3.1.0'
|
||||
def daggerVersion = '2.35'
|
||||
def daggerVersion = '2.35.1'
|
||||
def work_version = '2.5.0'
|
||||
def retrofit_version = '2.9.0'
|
||||
|
||||
|
@ -120,7 +120,7 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||
implementation "androidx.appcompat:appcompat:1.3.0"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
|
@ -169,7 +169,7 @@ dependencies {
|
|||
implementation 'com.otaliastudios:transcoder:0.10.3'
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.22'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.23'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.5.1'
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.matrix.android.sdk.internal.di.MatrixModule
|
|||
import org.matrix.android.sdk.internal.di.MatrixScope
|
||||
import org.matrix.android.sdk.internal.di.NetworkModule
|
||||
import org.matrix.android.sdk.internal.raw.RawModule
|
||||
import org.matrix.android.sdk.internal.util.system.SystemModule
|
||||
|
||||
@Component(modules = [
|
||||
TestModule::class,
|
||||
|
@ -33,6 +34,7 @@ import org.matrix.android.sdk.internal.raw.RawModule
|
|||
NetworkModule::class,
|
||||
AuthModule::class,
|
||||
RawModule::class,
|
||||
SystemModule::class,
|
||||
TestNetworkModule::class
|
||||
])
|
||||
@MatrixScope
|
||||
|
|
|
@ -226,12 +226,12 @@ class QrCodeTest : InstrumentedTest {
|
|||
|
||||
private fun checkHeader(byteArray: ByteArray) {
|
||||
// MATRIX
|
||||
byteArray[0] shouldBeEqualTo 'M'.toByte()
|
||||
byteArray[1] shouldBeEqualTo 'A'.toByte()
|
||||
byteArray[2] shouldBeEqualTo 'T'.toByte()
|
||||
byteArray[3] shouldBeEqualTo 'R'.toByte()
|
||||
byteArray[4] shouldBeEqualTo 'I'.toByte()
|
||||
byteArray[5] shouldBeEqualTo 'X'.toByte()
|
||||
byteArray[0] shouldBeEqualTo 'M'.code.toByte()
|
||||
byteArray[1] shouldBeEqualTo 'A'.code.toByte()
|
||||
byteArray[2] shouldBeEqualTo 'T'.code.toByte()
|
||||
byteArray[3] shouldBeEqualTo 'R'.code.toByte()
|
||||
byteArray[4] shouldBeEqualTo 'I'.code.toByte()
|
||||
byteArray[5] shouldBeEqualTo 'X'.code.toByte()
|
||||
|
||||
// Version
|
||||
byteArray[6] shouldBeEqualTo 2
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.securestorage
|
||||
|
||||
import android.os.Build
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.UUID
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class SecretStoringUtilsTest : InstrumentedTest {
|
||||
|
||||
private val buildVersionSdkIntProvider = TestBuildVersionSdkIntProvider()
|
||||
private val secretStoringUtils = SecretStoringUtils(context(), buildVersionSdkIntProvider)
|
||||
|
||||
companion object {
|
||||
const val TEST_STR = "This is something I want to store safely!"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStringNominalCaseApi21() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
|
||||
// Encrypt
|
||||
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias)
|
||||
// Decrypt
|
||||
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias)
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStringNominalCaseApi23() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
|
||||
// Encrypt
|
||||
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias)
|
||||
// Decrypt
|
||||
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias)
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStringNominalCaseApi30() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.R
|
||||
// Encrypt
|
||||
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias)
|
||||
// Decrypt
|
||||
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias)
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStringMigration21_23() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
|
||||
// Encrypt
|
||||
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias)
|
||||
|
||||
// Simulate a system upgrade
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
|
||||
|
||||
// Decrypt
|
||||
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias)
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObjectNominalCaseApi21() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
|
||||
|
||||
// Encrypt
|
||||
val encrypted = ByteArrayOutputStream().also { outputStream ->
|
||||
outputStream.use {
|
||||
secretStoringUtils.securelyStoreObject(TEST_STR, alias, it)
|
||||
}
|
||||
}
|
||||
.toByteArray()
|
||||
.toBase64NoPadding()
|
||||
// Decrypt
|
||||
val decrypted = encrypted.fromBase64().inputStream().use {
|
||||
secretStoringUtils.loadSecureSecret<String>(it, alias)
|
||||
}
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObjectNominalCaseApi23() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
|
||||
|
||||
// Encrypt
|
||||
val encrypted = ByteArrayOutputStream().also { outputStream ->
|
||||
outputStream.use {
|
||||
secretStoringUtils.securelyStoreObject(TEST_STR, alias, it)
|
||||
}
|
||||
}
|
||||
.toByteArray()
|
||||
.toBase64NoPadding()
|
||||
// Decrypt
|
||||
val decrypted = encrypted.fromBase64().inputStream().use {
|
||||
secretStoringUtils.loadSecureSecret<String>(it, alias)
|
||||
}
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObjectNominalCaseApi30() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.R
|
||||
|
||||
// Encrypt
|
||||
val encrypted = ByteArrayOutputStream().also { outputStream ->
|
||||
outputStream.use {
|
||||
secretStoringUtils.securelyStoreObject(TEST_STR, alias, it)
|
||||
}
|
||||
}
|
||||
.toByteArray()
|
||||
.toBase64NoPadding()
|
||||
// Decrypt
|
||||
val decrypted = encrypted.fromBase64().inputStream().use {
|
||||
secretStoringUtils.loadSecureSecret<String>(it, alias)
|
||||
}
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObjectMigration21_23() {
|
||||
val alias = generateAlias()
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
|
||||
|
||||
// Encrypt
|
||||
val encrypted = ByteArrayOutputStream().also { outputStream ->
|
||||
outputStream.use {
|
||||
secretStoringUtils.securelyStoreObject(TEST_STR, alias, it)
|
||||
}
|
||||
}
|
||||
.toByteArray()
|
||||
.toBase64NoPadding()
|
||||
|
||||
// Simulate a system upgrade
|
||||
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
|
||||
|
||||
// Decrypt
|
||||
val decrypted = encrypted.fromBase64().inputStream().use {
|
||||
secretStoringUtils.loadSecureSecret<String>(it, alias)
|
||||
}
|
||||
decrypted shouldBeEqualTo TEST_STR
|
||||
secretStoringUtils.safeDeleteKey(alias)
|
||||
}
|
||||
|
||||
private fun generateAlias() = UUID.randomUUID().toString()
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.securestorage
|
||||
|
||||
import org.matrix.android.sdk.internal.util.system.BuildVersionSdkIntProvider
|
||||
|
||||
class TestBuildVersionSdkIntProvider : BuildVersionSdkIntProvider {
|
||||
var value: Int = 0
|
||||
|
||||
override fun get() = value
|
||||
}
|
|
@ -51,11 +51,15 @@ interface AuthenticationService {
|
|||
|
||||
/**
|
||||
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
|
||||
*
|
||||
* See [LoginWizard] for more details
|
||||
*/
|
||||
fun getLoginWizard(): LoginWizard
|
||||
|
||||
/**
|
||||
* Return a RegistrationWizard, to create an matrix account on the homeserver. The login flow has to be retrieved first.
|
||||
*
|
||||
* See [RegistrationWizard] for more details.
|
||||
*/
|
||||
fun getRegistrationWizard(): RegistrationWizard
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ data class SsoIdentityProvider(
|
|||
*/
|
||||
@Json(name = "brand") val brand: String?
|
||||
|
||||
) : Parcelable {
|
||||
) : Parcelable, Comparable<SsoIdentityProvider> {
|
||||
|
||||
companion object {
|
||||
const val BRAND_GOOGLE = "org.matrix.google"
|
||||
|
@ -58,4 +58,25 @@ data class SsoIdentityProvider(
|
|||
const val BRAND_TWITTER = "org.matrix.twitter"
|
||||
const val BRAND_GITLAB = "org.matrix.gitlab"
|
||||
}
|
||||
|
||||
override fun compareTo(other: SsoIdentityProvider): Int {
|
||||
return other.toPriority().compareTo(toPriority())
|
||||
}
|
||||
|
||||
private fun toPriority(): Int {
|
||||
return when (brand) {
|
||||
// We are on Android, so user is more likely to have a Google account
|
||||
BRAND_GOOGLE -> 5
|
||||
// Facebook is also an important SSO provider
|
||||
BRAND_FACEBOOK -> 4
|
||||
// Twitter is more for professionals
|
||||
BRAND_TWITTER -> 3
|
||||
// Here it's very for techie people
|
||||
BRAND_GITHUB,
|
||||
BRAND_GITLAB -> 2
|
||||
// And finally, if the account has been created with an iPhone...
|
||||
BRAND_APPLE -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.auth.login
|
||||
|
||||
data class LoginProfileInfo(
|
||||
val matrixId: String,
|
||||
val displayName: String?,
|
||||
val fullAvatarUrl: String?
|
||||
)
|
|
@ -17,34 +17,51 @@
|
|||
package org.matrix.android.sdk.api.auth.login
|
||||
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
|
||||
/**
|
||||
* Set of methods to be able to login to an existing account on a homeserver.
|
||||
*
|
||||
* More documentation can be found in the file https://github.com/vector-im/element-android/blob/main/docs/signin.md
|
||||
*/
|
||||
interface LoginWizard {
|
||||
/**
|
||||
* Get some information about a matrixId: displayName and avatar url
|
||||
*/
|
||||
suspend fun getProfileInfo(matrixId: String): LoginProfileInfo
|
||||
|
||||
/**
|
||||
* @param login the login field
|
||||
* @param password the password field
|
||||
* Login to the homeserver.
|
||||
*
|
||||
* @param login the login field. Can be a user name, or a msisdn (email or phone number) associated to the account
|
||||
* @param password the password of the account
|
||||
* @param deviceName the initial device name
|
||||
* @param callback the matrix callback on which you'll receive the result of authentication.
|
||||
* @return a [Cancelable]
|
||||
* @return a [Session] if the login is successful
|
||||
*/
|
||||
suspend fun login(login: String,
|
||||
password: String,
|
||||
deviceName: String): Session
|
||||
|
||||
/**
|
||||
* Exchange a login token to an access token
|
||||
* Exchange a login token to an access token.
|
||||
*
|
||||
* @param loginToken login token, obtain when login has happen in a WebView, using SSO
|
||||
* @return a [Session] if the login is successful
|
||||
*/
|
||||
suspend fun loginWithToken(loginToken: String): Session
|
||||
|
||||
/**
|
||||
* Reset user password
|
||||
* Ask the homeserver to reset the user password. The password will not be reset until
|
||||
* [resetPasswordMailConfirmed] is successfully called.
|
||||
*
|
||||
* @param email an email previously associated to the account the user wants the password to be reset.
|
||||
* @param newPassword the desired new password
|
||||
*/
|
||||
suspend fun resetPassword(email: String,
|
||||
newPassword: String)
|
||||
|
||||
/**
|
||||
* Confirm the new password, once the user has checked their email
|
||||
* When this method succeed, tha account password will be effectively modified.
|
||||
*/
|
||||
suspend fun resetPasswordMailConfirmed()
|
||||
}
|
||||
|
|
|
@ -16,32 +16,98 @@
|
|||
|
||||
package org.matrix.android.sdk.api.auth.registration
|
||||
|
||||
/**
|
||||
* Set of methods to be able to create an account on a homeserver.
|
||||
*
|
||||
* Common scenario to register an account successfully:
|
||||
* - Call [getRegistrationFlow] to check that you application supports all the mandatory registration stages
|
||||
* - Call [createAccount] to start the account creation
|
||||
* - Fulfill all mandatory stages using the methods [performReCaptcha] [acceptTerms] [dummy], etc.
|
||||
*
|
||||
* More documentation can be found in the file https://github.com/vector-im/element-android/blob/main/docs/signup.md
|
||||
* and https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
|
||||
*/
|
||||
interface RegistrationWizard {
|
||||
|
||||
/**
|
||||
* Call this method to get the possible registration flow of the current homeserver.
|
||||
* It can be useful to ensure that your application implementation supports all the stages
|
||||
* required to create an account. If it is not the case, you will have to use the web fallback
|
||||
* to let the user create an account with your application.
|
||||
* See [org.matrix.android.sdk.api.auth.AuthenticationService.getFallbackUrl]
|
||||
*/
|
||||
suspend fun getRegistrationFlow(): RegistrationResult
|
||||
|
||||
/**
|
||||
* Can be call to check is the desired userName is available for registration on the current homeserver.
|
||||
* It may also fails if the desired userName is not correctly formatted or does not follow any restriction on
|
||||
* the homeserver. Ex: userName with only digits may be rejected.
|
||||
* @param userName the desired username. Ex: "alice"
|
||||
*/
|
||||
suspend fun registrationAvailable(userName: String): RegistrationAvailability
|
||||
|
||||
/**
|
||||
* This is the first method to call in order to create an account and start the registration process.
|
||||
*
|
||||
* @param userName the desired username. Ex: "alice"
|
||||
* @param password the desired password
|
||||
* @param initialDeviceDisplayName the device display name
|
||||
*/
|
||||
suspend fun createAccount(userName: String?,
|
||||
password: String?,
|
||||
initialDeviceDisplayName: String?): RegistrationResult
|
||||
|
||||
/**
|
||||
* Perform the "m.login.recaptcha" stage.
|
||||
*
|
||||
* @param response the response from ReCaptcha
|
||||
*/
|
||||
suspend fun performReCaptcha(response: String): RegistrationResult
|
||||
|
||||
/**
|
||||
* Perform the "m.login.terms" stage.
|
||||
*/
|
||||
suspend fun acceptTerms(): RegistrationResult
|
||||
|
||||
/**
|
||||
* Perform the "m.login.dummy" stage.
|
||||
*/
|
||||
suspend fun dummy(): RegistrationResult
|
||||
|
||||
/**
|
||||
* Perform the "m.login.email.identity" or "m.login.msisdn" stage.
|
||||
*
|
||||
* @param threePid the threePid to add to the account. If this is an email, the homeserver will send an email
|
||||
* to validate it. For a msisdn a SMS will be sent.
|
||||
*/
|
||||
suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult
|
||||
|
||||
/**
|
||||
* Ask the homeserver to send again the current threePid (email or msisdn).
|
||||
*/
|
||||
suspend fun sendAgainThreePid(): RegistrationResult
|
||||
|
||||
/**
|
||||
* Send the code received by SMS to validate a msisdn.
|
||||
* If the code is correct, the registration request will be executed to validate the msisdn.
|
||||
*/
|
||||
suspend fun handleValidateThreePid(code: String): RegistrationResult
|
||||
|
||||
/**
|
||||
* Useful to poll the homeserver when waiting for the email to be validated by the user.
|
||||
* Once the email is validated, this method will return successfully.
|
||||
* @param delayMillis the SDK can wait before sending the request
|
||||
*/
|
||||
suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult
|
||||
|
||||
suspend fun registrationAvailable(userName: String): RegistrationAvailability
|
||||
|
||||
/**
|
||||
* This is the current ThreePid, waiting for validation. The SDK will store it in database, so it can be
|
||||
* restored even if the app has been killed during the registration
|
||||
*/
|
||||
val currentThreePid: String?
|
||||
|
||||
// True when login and password has been sent with success to the homeserver
|
||||
/**
|
||||
* True when login and password have been sent with success to the homeserver, i.e. [createAccount] has been
|
||||
* called successfully.
|
||||
*/
|
||||
val isRegistrationStarted: Boolean
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.identity.IdentityService
|
|||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||
|
@ -233,6 +234,11 @@ interface Session :
|
|||
*/
|
||||
fun spaceService(): SpaceService
|
||||
|
||||
/**
|
||||
* Returns the open id service associated with the session
|
||||
*/
|
||||
fun openIdService(): OpenIdService
|
||||
|
||||
/**
|
||||
* Add a listener to the session.
|
||||
* @param listener the listener to add.
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.openid
|
||||
|
||||
interface OpenIdService {
|
||||
|
||||
/**
|
||||
* Gets an OpenID token object that the requester may supply to another service to verify their identity in Matrix.
|
||||
* The generated token is only valid for exchanging for user information from the federation API for OpenID.
|
||||
*/
|
||||
suspend fun getOpenIdToken(): OpenIdToken
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.openid
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OpenIdToken(
|
||||
/**
|
||||
* Required. An access token the consumer may use to verify the identity of the person who generated the token.
|
||||
* This is given to the federation API GET /openid/userinfo to verify the user's identity.
|
||||
*/
|
||||
@Json(name = "access_token")
|
||||
val accessToken: String,
|
||||
|
||||
/**
|
||||
* Required. The string "Bearer".
|
||||
*/
|
||||
@Json(name = "token_type")
|
||||
val tokenType: String,
|
||||
|
||||
/**
|
||||
* Required. The homeserver domain the consumer should use when attempting to verify the user's identity.
|
||||
*/
|
||||
@Json(name = "matrix_server_name")
|
||||
val matrixServerName: String,
|
||||
|
||||
/**
|
||||
* Required. The number of seconds before this token expires and a new one must be generated.
|
||||
*/
|
||||
@Json(name = "expires_in")
|
||||
val expiresIn: Int
|
||||
)
|
|
@ -83,7 +83,7 @@ interface Room :
|
|||
* @param beforeLimit how many events before the result are returned.
|
||||
* @param afterLimit how many events after the result are returned.
|
||||
* @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
|
||||
* @param callback Callback to get the search result
|
||||
* @return The search result
|
||||
*/
|
||||
suspend fun search(searchTerm: String,
|
||||
nextBatch: String?,
|
||||
|
|
|
@ -30,5 +30,7 @@ data class SpaceChildInfo(
|
|||
val autoJoin: Boolean,
|
||||
val viaServers: List<String>,
|
||||
val parentRoomId: String?,
|
||||
val suggested: Boolean?
|
||||
val suggested: Boolean?,
|
||||
val canonicalAlias: String?,
|
||||
val aliases: List<String>?
|
||||
)
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.annotation.SuppressLint
|
|||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.util.safeCapitalize
|
||||
|
||||
/**
|
||||
* Ref: https://github.com/matrix-org/matrix-doc/issues/1236
|
||||
|
@ -39,6 +40,6 @@ data class WidgetContent(
|
|||
|
||||
@SuppressLint("DefaultLocale")
|
||||
fun getHumanName(): String {
|
||||
return (name ?: type ?: "").capitalize()
|
||||
return (name ?: type ?: "").safeCapitalize()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,22 +117,22 @@ sealed class MatrixItem(
|
|||
var first = dn[startIndex]
|
||||
|
||||
// LEFT-TO-RIGHT MARK
|
||||
if (dn.length >= 2 && 0x200e == first.toInt()) {
|
||||
if (dn.length >= 2 && 0x200e == first.code) {
|
||||
startIndex++
|
||||
first = dn[startIndex]
|
||||
}
|
||||
|
||||
// check if it’s the start of a surrogate pair
|
||||
if (first.toInt() in 0xD800..0xDBFF && dn.length > startIndex + 1) {
|
||||
if (first.code in 0xD800..0xDBFF && dn.length > startIndex + 1) {
|
||||
val second = dn[startIndex + 1]
|
||||
if (second.toInt() in 0xDC00..0xDFFF) {
|
||||
if (second.code in 0xDC00..0xDFFF) {
|
||||
length++
|
||||
}
|
||||
}
|
||||
|
||||
dn.substring(startIndex, startIndex + length)
|
||||
}
|
||||
.toUpperCase(Locale.ROOT)
|
||||
.uppercase(Locale.ROOT)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -159,4 +159,4 @@ fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName,
|
|||
|
||||
fun SenderInfo.toMatrixItem() = MatrixItem.UserItem(userId, disambiguatedDisplayName, avatarUrl)
|
||||
|
||||
fun SpaceChildInfo.toMatrixItem() = MatrixItem.RoomItem(childRoomId, name, avatarUrl)
|
||||
fun SpaceChildInfo.toMatrixItem() = MatrixItem.RoomItem(childRoomId, name ?: canonicalAlias ?: "", avatarUrl)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.auth
|
||||
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.auth.data.Availability
|
||||
import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
|
||||
import org.matrix.android.sdk.internal.auth.data.PasswordLoginParams
|
||||
|
@ -73,6 +74,15 @@ internal interface AuthAPI {
|
|||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register/available")
|
||||
suspend fun registerAvailable(@Query("username") username: String): Availability
|
||||
|
||||
/**
|
||||
* Get the combined profile information for this user.
|
||||
* This API may be used to fetch the user's own profile information or other users; either locally or on remote homeservers.
|
||||
* This API may return keys which are not limited to displayname or avatar_url.
|
||||
* @param userId the user id to fetch profile info
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}")
|
||||
suspend fun getProfile(@Path("userId") userId: String): JsonDict
|
||||
|
||||
/**
|
||||
* Add 3Pid during registration
|
||||
* Ref: https://gist.github.com/jryans/839a09bf0c5a70e2f36ed990d50ed928
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.auth.login
|
||||
|
||||
import android.util.Patterns
|
||||
import org.matrix.android.sdk.api.auth.login.LoginProfileInfo
|
||||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
@ -30,6 +31,7 @@ import org.matrix.android.sdk.internal.auth.db.PendingSessionData
|
|||
import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationParams
|
||||
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
|
||||
|
||||
internal class DefaultLoginWizard(
|
||||
private val authAPI: AuthAPI,
|
||||
|
@ -39,6 +41,15 @@ internal class DefaultLoginWizard(
|
|||
|
||||
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
|
||||
|
||||
private val getProfileTask: GetProfileTask = DefaultGetProfileTask(
|
||||
authAPI,
|
||||
DefaultContentUrlResolver(pendingSessionData.homeServerConnectionConfig)
|
||||
)
|
||||
|
||||
override suspend fun getProfileInfo(matrixId: String): LoginProfileInfo {
|
||||
return getProfileTask.execute(GetProfileTask.Params(matrixId))
|
||||
}
|
||||
|
||||
override suspend fun login(login: String,
|
||||
password: String,
|
||||
deviceName: String): Session {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.auth.login
|
||||
|
||||
import org.matrix.android.sdk.api.auth.login.LoginProfileInfo
|
||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.internal.auth.AuthAPI
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
|
||||
internal interface GetProfileTask : Task<GetProfileTask.Params, LoginProfileInfo> {
|
||||
data class Params(
|
||||
val userId: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultGetProfileTask(
|
||||
private val authAPI: AuthAPI,
|
||||
private val contentUrlResolver: ContentUrlResolver
|
||||
) : GetProfileTask {
|
||||
|
||||
override suspend fun execute(params: GetProfileTask.Params): LoginProfileInfo {
|
||||
val info = executeRequest(null) {
|
||||
authAPI.getProfile(params.userId)
|
||||
}
|
||||
|
||||
return LoginProfileInfo(
|
||||
matrixId = params.userId,
|
||||
displayName = info[ProfileService.DISPLAY_NAME_KEY] as? String,
|
||||
fullAvatarUrl = contentUrlResolver.resolveFullSize(info[ProfileService.AVATAR_URL_KEY] as? String)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -545,14 +545,14 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
|
||||
|
||||
if (!existingAlgorithm.isNullOrEmpty() && existingAlgorithm != algorithm) {
|
||||
Timber.e("## CRYPTO | setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
|
||||
Timber.e("## CRYPTO | setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
|
||||
return false
|
||||
}
|
||||
|
||||
val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm)
|
||||
|
||||
if (!encryptingClass) {
|
||||
Timber.e("## CRYPTO | setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
|
||||
Timber.e("## CRYPTO | setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -649,17 +649,17 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
val safeAlgorithm = alg
|
||||
if (safeAlgorithm != null) {
|
||||
val t0 = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | encryptEventContent() starts")
|
||||
Timber.v("## CRYPTO | encryptEventContent() starts")
|
||||
runCatching {
|
||||
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||
Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
|
||||
}.foldToCallback(callback)
|
||||
} else {
|
||||
val algorithm = getEncryptionAlgorithm(roomId)
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON,
|
||||
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
|
||||
Timber.e("## CRYPTO | encryptEventContent() : $reason")
|
||||
Timber.e("## CRYPTO | encryptEventContent() : $reason")
|
||||
callback.onFailure(Failure.CryptoError(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_ENCRYPT, reason)))
|
||||
}
|
||||
}
|
||||
|
@ -769,7 +769,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
}
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
|
||||
if (alg == null) {
|
||||
Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
|
||||
Timber.e("## CRYPTO | GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
|
||||
return
|
||||
}
|
||||
alg.onRoomKeyEvent(event, keysBackupService)
|
||||
|
@ -777,7 +777,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
private fun onKeyWithHeldReceived(event: Event) {
|
||||
val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also {
|
||||
Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
|
||||
Timber.i("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
|
||||
}
|
||||
Timber.i("## CRYPTO | onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>")
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm)
|
||||
|
@ -790,16 +790,16 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onSecretSendReceived(event: Event) {
|
||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() from ${event.senderId} : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||
if (!event.isEncrypted()) {
|
||||
// secret send messages must be encrypted
|
||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() :Received unencrypted secret send event")
|
||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() :Received unencrypted secret send event")
|
||||
return
|
||||
}
|
||||
|
||||
// Was that sent by us?
|
||||
if (event.senderId != userId) {
|
||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
|
||||
Timber.e("## CRYPTO | GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -809,13 +809,13 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
.getOutgoingSecretKeyRequests().firstOrNull { it.requestId == secretContent.requestId }
|
||||
|
||||
if (existingRequest == null) {
|
||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
|
||||
Timber.i("## CRYPTO | GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
|
||||
return
|
||||
}
|
||||
|
||||
if (!handleSDKLevelGossip(existingRequest.secretName, secretContent.secretValue)) {
|
||||
// TODO Ask to application layer?
|
||||
Timber.v("## CRYPTO | onSecretSend() : secret not handled by SDK")
|
||||
Timber.v("## CRYPTO | onSecretSend() : secret not handled by SDK")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,13 +972,13 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||
runCatching {
|
||||
withContext(coroutineDispatchers.crypto) {
|
||||
Timber.v("## CRYPTO | importRoomKeys starts")
|
||||
Timber.v("## CRYPTO | importRoomKeys starts")
|
||||
|
||||
val t0 = System.currentTimeMillis()
|
||||
val roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password)
|
||||
val t1 = System.currentTimeMillis()
|
||||
|
||||
Timber.v("## CRYPTO | importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
||||
Timber.v("## CRYPTO | importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
||||
|
||||
val importedSessions = MoshiProvider.providesMoshi()
|
||||
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
|
||||
|
@ -986,7 +986,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
val t2 = System.currentTimeMillis()
|
||||
|
||||
Timber.v("## CRYPTO | importRoomKeys : JSON parsing ${t2 - t1} ms")
|
||||
Timber.v("## CRYPTO | importRoomKeys : JSON parsing ${t2 - t1} ms")
|
||||
|
||||
if (importedSessions == null) {
|
||||
throw Exception("Error")
|
||||
|
@ -1125,7 +1125,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
*/
|
||||
override fun reRequestRoomKeyForEvent(event: Event) {
|
||||
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.e("## CRYPTO | reRequestRoomKeyForEvent Failed to re-request key, null content")
|
||||
Timber.e("## CRYPTO | reRequestRoomKeyForEvent Failed to re-request key, null content")
|
||||
}
|
||||
|
||||
val requestBody = RoomKeyRequestBody(
|
||||
|
@ -1140,18 +1140,18 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
override fun requestRoomKeyForEvent(event: Event) {
|
||||
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.e("## CRYPTO | requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
|
||||
Timber.e("## CRYPTO | requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
|
||||
}
|
||||
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
// if (!isStarted()) {
|
||||
// Timber.v("## CRYPTO | requestRoomKeyForEvent() : wait after e2e init")
|
||||
// Timber.v("## CRYPTO | requestRoomKeyForEvent() : wait after e2e init")
|
||||
// internalStart(false)
|
||||
// }
|
||||
roomDecryptorProvider
|
||||
.getOrCreateRoomDecryptor(event.roomId, wireContent.algorithm)
|
||||
?.requestKeysForEvent(event, false) ?: run {
|
||||
Timber.v("## CRYPTO | requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
|
||||
Timber.v("## CRYPTO | requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1180,11 +1180,11 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
// val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
||||
// val now = System.currentTimeMillis()
|
||||
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
||||
// lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
|
||||
//
|
||||
// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
|
@ -1201,7 +1201,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
// val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
// val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
// sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
|
||||
// Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}")
|
||||
// Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}")
|
||||
// val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
// sendToDeviceTask.execute(sendToDeviceParams)
|
||||
// }
|
||||
|
@ -1290,12 +1290,12 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
override fun prepareToEncrypt(roomId: String, callback: MatrixCallback<Unit>) {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
Timber.d("## CRYPTO | prepareToEncrypt() : Check room members up to date")
|
||||
Timber.d("## CRYPTO | prepareToEncrypt() : Check room members up to date")
|
||||
// Ensure to load all room members
|
||||
try {
|
||||
loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() : Failed to load room members")
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() : Failed to load room members")
|
||||
callback.onFailure(failure)
|
||||
return@launch
|
||||
}
|
||||
|
@ -1308,7 +1308,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
if (alg == null) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, MXCryptoError.NO_MORE_ALGORITHM_REASON)
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() : $reason")
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() : $reason")
|
||||
callback.onFailure(IllegalArgumentException("Missing algorithm"))
|
||||
return@launch
|
||||
}
|
||||
|
@ -1318,7 +1318,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
}.fold(
|
||||
{ callback.onSuccess(Unit) },
|
||||
{
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() failed.")
|
||||
Timber.e("## CRYPTO | prepareToEncrypt() failed.")
|
||||
callback.onFailure(it)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -111,7 +111,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
res = !notReadyToRetryHS.contains(userId.substringAfter(':'))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## CRYPTO | canRetryKeysDownload() failed")
|
||||
Timber.e(e, "## CRYPTO | canRetryKeysDownload() failed")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
|
||||
for (userId in userIds) {
|
||||
if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) {
|
||||
Timber.v("## CRYPTO | startTrackingDeviceList() : Now tracking device list for $userId")
|
||||
Timber.v("## CRYPTO | startTrackingDeviceList() : Now tracking device list for $userId")
|
||||
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||
isUpdated = true
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
|
||||
for (userId in changed) {
|
||||
if (deviceTrackingStatuses.containsKey(userId)) {
|
||||
Timber.v("## CRYPTO | handleDeviceListsChanges() : Marking device list outdated for $userId")
|
||||
Timber.v("## CRYPTO | handleDeviceListsChanges() : Marking device list outdated for $userId")
|
||||
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||
isUpdated = true
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
|
||||
for (userId in left) {
|
||||
if (deviceTrackingStatuses.containsKey(userId)) {
|
||||
Timber.v("## CRYPTO | handleDeviceListsChanges() : No longer tracking device list for $userId")
|
||||
Timber.v("## CRYPTO | handleDeviceListsChanges() : No longer tracking device list for $userId")
|
||||
deviceTrackingStatuses[userId] = TRACKING_STATUS_NOT_TRACKED
|
||||
isUpdated = true
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
* @param forceDownload Always download the keys even if cached.
|
||||
*/
|
||||
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
Timber.v("## CRYPTO | downloadKeys() : forceDownload $forceDownload : $userIds")
|
||||
Timber.v("## CRYPTO | downloadKeys() : forceDownload $forceDownload : $userIds")
|
||||
// Map from userId -> deviceId -> MXDeviceInfo
|
||||
val stored = MXUsersDevicesMap<CryptoDeviceInfo>()
|
||||
|
||||
|
@ -305,13 +305,13 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
}
|
||||
}
|
||||
return if (downloadUsers.isEmpty()) {
|
||||
Timber.v("## CRYPTO | downloadKeys() : no new user device")
|
||||
Timber.v("## CRYPTO | downloadKeys() : no new user device")
|
||||
stored
|
||||
} else {
|
||||
Timber.v("## CRYPTO | downloadKeys() : starts")
|
||||
Timber.v("## CRYPTO | downloadKeys() : starts")
|
||||
val t0 = System.currentTimeMillis()
|
||||
val result = doKeyDownloadForUsers(downloadUsers)
|
||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
result.also {
|
||||
it.addEntriesFromMap(stored)
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
* @param downloadUsers the user ids list
|
||||
*/
|
||||
private suspend fun doKeyDownloadForUsers(downloadUsers: List<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : doKeyDownloadForUsers ${downloadUsers.logLimit()}")
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : doKeyDownloadForUsers ${downloadUsers.logLimit()}")
|
||||
// get the user ids which did not already trigger a keys download
|
||||
val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) }
|
||||
if (filteredUsers.isEmpty()) {
|
||||
|
@ -335,16 +335,16 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
val response = try {
|
||||
downloadKeysForUsersTask.execute(params)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
|
||||
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
|
||||
onKeysDownloadFailed(filteredUsers)
|
||||
throw throwable
|
||||
}
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||
for (userId in filteredUsers) {
|
||||
// al devices =
|
||||
val models = response.deviceKeys?.get(userId)?.mapValues { entry -> CryptoInfoMapper.map(entry.value) }
|
||||
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for $userId : $models")
|
||||
Timber.v("## CRYPTO | doKeyDownloadForUsers() : Got keys for $userId : $models")
|
||||
if (!models.isNullOrEmpty()) {
|
||||
val workingCopy = models.toMutableMap()
|
||||
for ((deviceId, deviceInfo) in models) {
|
||||
|
@ -377,13 +377,13 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
}
|
||||
|
||||
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
|
||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
|
||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
|
||||
}
|
||||
val selfSigningKey = response.selfSigningKeys?.get(userId)?.toCryptoModel()?.also {
|
||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : SSK ${it.unpaddedBase64PublicKey}")
|
||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : SSK ${it.unpaddedBase64PublicKey}")
|
||||
}
|
||||
val userSigningKey = response.userSigningKeys?.get(userId)?.toCryptoModel()?.also {
|
||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : USK ${it.unpaddedBase64PublicKey}")
|
||||
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : USK ${it.unpaddedBase64PublicKey}")
|
||||
}
|
||||
cryptoStore.storeUserCrossSigningKeys(
|
||||
userId,
|
||||
|
@ -411,28 +411,28 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
*/
|
||||
private fun validateDeviceKeys(deviceKeys: CryptoDeviceInfo?, userId: String, deviceId: String, previouslyStoredDeviceKeys: CryptoDeviceInfo?): Boolean {
|
||||
if (null == deviceKeys) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys is null from $userId:$deviceId")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys is null from $userId:$deviceId")
|
||||
return false
|
||||
}
|
||||
|
||||
if (null == deviceKeys.keys) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.keys is null from $userId:$deviceId")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.keys is null from $userId:$deviceId")
|
||||
return false
|
||||
}
|
||||
|
||||
if (null == deviceKeys.signatures) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.signatures is null from $userId:$deviceId")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : deviceKeys.signatures is null from $userId:$deviceId")
|
||||
return false
|
||||
}
|
||||
|
||||
// Check that the user_id and device_id in the received deviceKeys are correct
|
||||
if (deviceKeys.userId != userId) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched user_id ${deviceKeys.userId} from $userId:$deviceId")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched user_id ${deviceKeys.userId} from $userId:$deviceId")
|
||||
return false
|
||||
}
|
||||
|
||||
if (deviceKeys.deviceId != deviceId) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched device_id ${deviceKeys.deviceId} from $userId:$deviceId")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Mismatched device_id ${deviceKeys.deviceId} from $userId:$deviceId")
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -440,21 +440,21 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
val signKey = deviceKeys.keys[signKeyId]
|
||||
|
||||
if (null == signKey) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no ed25519 key")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no ed25519 key")
|
||||
return false
|
||||
}
|
||||
|
||||
val signatureMap = deviceKeys.signatures[userId]
|
||||
|
||||
if (null == signatureMap) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no map for $userId")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} has no map for $userId")
|
||||
return false
|
||||
}
|
||||
|
||||
val signature = signatureMap[signKeyId]
|
||||
|
||||
if (null == signature) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} is not signed")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Device $userId:${deviceKeys.deviceId} is not signed")
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -469,7 +469,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
}
|
||||
|
||||
if (!isVerified) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":"
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":"
|
||||
+ deviceKeys.deviceId + " with error " + errorMessage)
|
||||
return false
|
||||
}
|
||||
|
@ -480,12 +480,12 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
// best off sticking with the original keys.
|
||||
//
|
||||
// Should we warn the user about it somehow?
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":"
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":"
|
||||
+ deviceKeys.deviceId + " has changed : "
|
||||
+ previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
|
||||
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -499,7 +499,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
* This method must be called on getEncryptingThreadHandler() thread.
|
||||
*/
|
||||
suspend fun refreshOutdatedDeviceLists() {
|
||||
Timber.v("## CRYPTO | refreshOutdatedDeviceLists()")
|
||||
Timber.v("## CRYPTO | refreshOutdatedDeviceLists()")
|
||||
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
||||
|
||||
val users = deviceTrackingStatuses.keys.filterTo(mutableListOf()) { userId ->
|
||||
|
@ -518,10 +518,10 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||
doKeyDownloadForUsers(users)
|
||||
}.fold(
|
||||
{
|
||||
Timber.v("## CRYPTO | refreshOutdatedDeviceLists() : done")
|
||||
Timber.v("## CRYPTO | refreshOutdatedDeviceLists() : done")
|
||||
},
|
||||
{
|
||||
Timber.e(it, "## CRYPTO | refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
||||
Timber.e(it, "## CRYPTO | refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -92,20 +92,20 @@ internal class EventDecryptor @Inject constructor(
|
|||
private fun internalDecryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||
val eventContent = event.content
|
||||
if (eventContent == null) {
|
||||
Timber.e("## CRYPTO | decryptEvent : empty event content")
|
||||
Timber.e("## CRYPTO | decryptEvent : empty event content")
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||
} else {
|
||||
val algorithm = eventContent["algorithm"]?.toString()
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
|
||||
if (alg == null) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm)
|
||||
Timber.e("## CRYPTO | decryptEvent() : $reason")
|
||||
Timber.e("## CRYPTO | decryptEvent() : $reason")
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
|
||||
} else {
|
||||
try {
|
||||
return alg.decryptEvent(event, timeline)
|
||||
} catch (mxCryptoError: MXCryptoError) {
|
||||
Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError")
|
||||
Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError")
|
||||
if (algorithm == MXCRYPTO_ALGORITHM_OLM) {
|
||||
if (mxCryptoError is MXCryptoError.Base
|
||||
&& mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) {
|
||||
|
@ -119,7 +119,7 @@ internal class EventDecryptor @Inject constructor(
|
|||
markOlmSessionForUnwedging(event.senderId ?: "", it)
|
||||
}
|
||||
?: run {
|
||||
Timber.i("## CRYPTO | internalDecryptEvent() : Failed to find sender crypto device for unwedging")
|
||||
Timber.i("## CRYPTO | internalDecryptEvent() : Failed to find sender crypto device for unwedging")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,18 +137,18 @@ internal class EventDecryptor @Inject constructor(
|
|||
val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||
Timber.w("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||
Timber.w("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||
return
|
||||
}
|
||||
|
||||
Timber.i("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
||||
Timber.i("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
||||
lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
|
||||
|
||||
// offload this from crypto thread (?)
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.computation) {
|
||||
val ensured = ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true)
|
||||
|
||||
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : ensureOlmSessionsForDevicesAction isEmpty:${ensured.isEmpty}")
|
||||
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : ensureOlmSessionsForDevicesAction isEmpty:${ensured.isEmpty}")
|
||||
|
||||
// Now send a blank message on that session so the other side knows about it.
|
||||
// (The keyshare request is sent in the clear so that won't do)
|
||||
|
@ -161,13 +161,13 @@ internal class EventDecryptor @Inject constructor(
|
|||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
|
||||
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : sending dummy to $senderId:${deviceInfo.deviceId}")
|
||||
Timber.i("## CRYPTO | markOlmSessionForUnwedging() : sending dummy to $senderId:${deviceInfo.deviceId}")
|
||||
withContext(coroutineDispatchers.io) {
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## CRYPTO | markOlmSessionForUnwedging() : failed to send dummy to $senderId:${deviceInfo.deviceId}")
|
||||
Timber.e(failure, "## CRYPTO | markOlmSessionForUnwedging() : failed to send dummy to $senderId:${deviceInfo.deviceId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
|
||||
@Throws(MXCryptoError::class)
|
||||
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult {
|
||||
Timber.v("## CRYPTO | decryptEvent ${event.eventId} , requestKeysOnFail:$requestKeysOnFail")
|
||||
Timber.v("## CRYPTO | decryptEvent ${event.eventId}, requestKeysOnFail:$requestKeysOnFail")
|
||||
if (event.roomId.isNullOrBlank()) {
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ internal class MXMegolmDecryption(private val userId: String,
|
|||
},
|
||||
{
|
||||
// TODO
|
||||
Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body")
|
||||
Timber.e(it, "## CRYPTO | shareKeysWithDevice: failed to get session for request $body")
|
||||
}
|
||||
|
||||
)
|
||||
|
|
|
@ -80,9 +80,9 @@ internal class MXMegolmEncryption(
|
|||
eventType: String,
|
||||
userIds: List<String>): Content {
|
||||
val ts = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
|
||||
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
|
||||
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
|
||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||
|
||||
return encryptContent(outboundSession, eventType, eventContent)
|
||||
|
@ -91,7 +91,7 @@ internal class MXMegolmEncryption(
|
|||
// annoyingly we have to serialize again the saved outbound session to store message index :/
|
||||
// if not we would see duplicate message index errors
|
||||
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
|
||||
Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis")
|
||||
Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,13 +118,13 @@ internal class MXMegolmEncryption(
|
|||
|
||||
override suspend fun preshareKey(userIds: List<String>) {
|
||||
val ts = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | preshareKey : getDevicesInRoom")
|
||||
Timber.v("## CRYPTO | preshareKey : getDevicesInRoom")
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||
|
||||
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
||||
|
||||
Timber.v("## CRYPTO | preshareKey ${System.currentTimeMillis() - ts} millis")
|
||||
Timber.v("## CRYPTO | preshareKey ${System.currentTimeMillis() - ts} millis")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +133,7 @@ internal class MXMegolmEncryption(
|
|||
* @return the session description
|
||||
*/
|
||||
private fun prepareNewSessionInRoom(): MXOutboundSessionInfo {
|
||||
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
|
||||
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
|
||||
val sessionId = olmDevice.createOutboundGroupSessionForRoom(roomId)
|
||||
|
||||
val keysClaimedMap = HashMap<String, String>()
|
||||
|
@ -153,7 +153,7 @@ internal class MXMegolmEncryption(
|
|||
* @param devicesInRoom the devices list
|
||||
*/
|
||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
|
||||
Timber.v("## CRYPTO | ensureOutboundSession start")
|
||||
Timber.v("## CRYPTO | ensureOutboundSession start")
|
||||
var session = outboundSession
|
||||
if (session == null
|
||||
// Need to make a brand new session?
|
||||
|
@ -190,7 +190,7 @@ internal class MXMegolmEncryption(
|
|||
devicesByUsers: Map<String, List<CryptoDeviceInfo>>) {
|
||||
// nothing to send, the task is done
|
||||
if (devicesByUsers.isEmpty()) {
|
||||
Timber.v("## CRYPTO | shareKey() : nothing more to do")
|
||||
Timber.v("## CRYPTO | shareKey() : nothing more to do")
|
||||
return
|
||||
}
|
||||
// reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user)
|
||||
|
@ -203,7 +203,7 @@ internal class MXMegolmEncryption(
|
|||
break
|
||||
}
|
||||
}
|
||||
Timber.v("## CRYPTO | shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
|
||||
Timber.v("## CRYPTO | shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
|
||||
shareUserDevicesKey(session, subMap)
|
||||
val remainingDevices = devicesByUsers - subMap.keys
|
||||
shareKey(session, remainingDevices)
|
||||
|
@ -232,11 +232,11 @@ internal class MXMegolmEncryption(
|
|||
payload["content"] = submap
|
||||
|
||||
var t0 = System.currentTimeMillis()
|
||||
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
|
||||
Timber.v("## CRYPTO | shareUserDevicesKey() : starts")
|
||||
|
||||
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
Timber.v(
|
||||
"""## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
||||
"""## CRYPTO | shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
||||
.trimMargin()
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
|
@ -257,7 +257,7 @@ internal class MXMegolmEncryption(
|
|||
noOlmToNotify.add(UserDevice(userId, deviceID))
|
||||
continue
|
||||
}
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
|
||||
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo)))
|
||||
haveTargets = true
|
||||
}
|
||||
|
@ -289,17 +289,17 @@ internal class MXMegolmEncryption(
|
|||
|
||||
if (haveTargets) {
|
||||
t0 = System.currentTimeMillis()
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||
try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
} catch (failure: Throwable) {
|
||||
// What to do here...
|
||||
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
|
||||
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
|
||||
}
|
||||
} else {
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
|
||||
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
|
||||
}
|
||||
|
||||
if (noOlmToNotify.isNotEmpty()) {
|
||||
|
@ -317,7 +317,7 @@ internal class MXMegolmEncryption(
|
|||
sessionId: String,
|
||||
senderKey: String?,
|
||||
code: WithHeldCode) {
|
||||
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code")
|
||||
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code")
|
||||
val withHeldContent = RoomKeyWithHeldContent(
|
||||
roomId = roomId,
|
||||
senderKey = senderKey,
|
||||
|
@ -336,7 +336,7 @@ internal class MXMegolmEncryption(
|
|||
try {
|
||||
sendToDeviceTask.execute(params)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ")
|
||||
Timber.e("## CRYPTO | notifyKeyWithHeld() : Failed to notify withheld key for $targets session: $sessionId ")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +473,7 @@ internal class MXMegolmEncryption(
|
|||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId")
|
||||
Timber.i("## CRYPTO | reshareKey() : sending session $sessionId to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
return try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
|
|
|
@ -57,7 +57,7 @@ object HkdfSha256 {
|
|||
|
||||
/*
|
||||
The output OKM is calculated as follows:
|
||||
Notation | -> When the message is composed of several elements we use concatenation (denoted |) in the second argument;
|
||||
Notation | -> When the message is composed of several elements we use concatenation (denoted |) in the second argument;
|
||||
|
||||
|
||||
N = ceil(L/HashLen)
|
||||
|
|
|
@ -345,7 +345,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||
}
|
||||
|
||||
protected fun hashUsingAgreedHashMethod(toHash: String): String? {
|
||||
if ("sha256" == accepted?.hash?.toLowerCase(Locale.ROOT)) {
|
||||
if ("sha256" == accepted?.hash?.lowercase(Locale.ROOT)) {
|
||||
val olmUtil = OlmUtility()
|
||||
val hashBytes = olmUtil.sha256(toHash)
|
||||
olmUtil.releaseUtility()
|
||||
|
@ -355,7 +355,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
|||
}
|
||||
|
||||
private fun macUsingAgreedMethod(message: String, info: String): String? {
|
||||
return when (accepted?.messageAuthenticationCode?.toLowerCase(Locale.ROOT)) {
|
||||
return when (accepted?.messageAuthenticationCode?.lowercase(Locale.ROOT)) {
|
||||
SAS_MAC_SHA256_LONGKDF -> getSAS().calculateMacLongKdf(message, info)
|
||||
SAS_MAC_SHA256 -> getSAS().calculateMac(message, info)
|
||||
else -> null
|
||||
|
|
|
@ -48,7 +48,7 @@ fun QrCodeData.toEncodedString(): String {
|
|||
|
||||
// TransactionId
|
||||
transactionId.forEach {
|
||||
result += it.toByte()
|
||||
result += it.code.toByte()
|
||||
}
|
||||
|
||||
// Keys
|
||||
|
|
|
@ -71,7 +71,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
|
|||
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
|
||||
val toStore = secretStoringUtils.securelyStoreString(encodedKey, alias)
|
||||
sharedPreferences.edit {
|
||||
putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING))
|
||||
putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore, Base64.NO_PADDING))
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
|
|||
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
|
||||
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
|
||||
val b64 = secretStoringUtils.loadSecureSecret(encryptedKey, alias)
|
||||
return Base64.decode(b64!!, Base64.NO_PADDING)
|
||||
return Base64.decode(b64, Base64.NO_PADDING)
|
||||
}
|
||||
|
||||
fun configureEncryption(realmConfigurationBuilder: RealmConfiguration.Builder, alias: String) {
|
||||
|
|
|
@ -49,7 +49,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
const val SESSION_STORE_SCHEMA_SC_VERSION = 2L
|
||||
const val SESSION_STORE_SCHEMA_SC_VERSION_OFFSET = (1L shl 12)
|
||||
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 12L +
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 13L +
|
||||
SESSION_STORE_SCHEMA_SC_VERSION * SESSION_STORE_SCHEMA_SC_VERSION_OFFSET
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
if (oldVersion <= 9) migrateTo10(realm)
|
||||
if (oldVersion <= 10) migrateTo11(realm)
|
||||
if (oldVersion <= 11) migrateTo12(realm)
|
||||
if (oldVersion <= 12) migrateTo13(realm)
|
||||
|
||||
if (oldScVersion <= 0) migrateToSc1(realm)
|
||||
if (oldScVersion <= 1) migrateToSc2(realm)
|
||||
|
@ -302,4 +303,14 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
|
||||
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
|
||||
}
|
||||
|
||||
private fun migrateTo13(realm: DynamicRealm) {
|
||||
Timber.d("Step 12 -> 13")
|
||||
|
||||
// Fix issue with the nightly build. Eventually play again the migration which has been included in migrateTo12()
|
||||
realm.schema.get("SpaceChildSummaryEntity")
|
||||
?.takeIf { !it.hasField(SpaceChildSummaryEntityFields.SUGGESTED) }
|
||||
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
|
||||
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,9 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
|||
autoJoin = it.autoJoin ?: false,
|
||||
viaServers = it.viaServers.toList(),
|
||||
parentRoomId = roomSummaryEntity.roomId,
|
||||
suggested = it.suggested
|
||||
suggested = it.suggested,
|
||||
canonicalAlias = it.childSummaryEntity?.canonicalAlias,
|
||||
aliases = it.childSummaryEntity?.aliases?.toList()
|
||||
)
|
||||
},
|
||||
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList()
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.session.TestInterceptor
|
|||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.internal.util.system.SystemModule
|
||||
import org.matrix.olm.OlmManager
|
||||
import java.io.File
|
||||
|
||||
|
@ -44,6 +45,7 @@ import java.io.File
|
|||
NetworkModule::class,
|
||||
AuthModule::class,
|
||||
RawModule::class,
|
||||
SystemModule::class,
|
||||
NoOpTestModule::class
|
||||
])
|
||||
@MatrixScope
|
||||
|
|
|
@ -291,7 +291,7 @@ internal class DefaultFileService @Inject constructor(
|
|||
Timber.v("Get size of ${it.absolutePath}")
|
||||
true
|
||||
}
|
||||
.sumBy { it.length().toInt() }
|
||||
.sumOf { it.length().toInt() }
|
||||
}
|
||||
|
||||
override fun clearCache() {
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesServi
|
|||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||
|
@ -125,6 +126,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val thirdPartyService: Lazy<ThirdPartyService>,
|
||||
private val callSignalingService: Lazy<CallSignalingService>,
|
||||
private val spaceService: Lazy<SpaceService>,
|
||||
private val openIdService: Lazy<OpenIdService>,
|
||||
@UnauthenticatedWithCertificate
|
||||
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>
|
||||
) : Session,
|
||||
|
@ -289,6 +291,8 @@ internal class DefaultSession @Inject constructor(
|
|||
|
||||
override fun spaceService(): SpaceService = spaceService.get()
|
||||
|
||||
override fun openIdService(): OpenIdService = openIdService.get()
|
||||
|
||||
override fun getOkHttpClient(): OkHttpClient {
|
||||
return unauthenticatedWithCertificateOkHttpClient.get()
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataModul
|
|||
import org.matrix.android.sdk.internal.session.widgets.WidgetModule
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.internal.util.system.SystemModule
|
||||
|
||||
@Component(dependencies = [MatrixComponent::class],
|
||||
modules = [
|
||||
|
@ -80,6 +81,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
|||
CacheModule::class,
|
||||
MediaModule::class,
|
||||
CryptoModule::class,
|
||||
SystemModule::class,
|
||||
PushersModule::class,
|
||||
OpenIdModule::class,
|
||||
WidgetModule::class,
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.accountdata.AccountDataService
|
|||
import org.matrix.android.sdk.api.session.events.EventService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.securestorage.SecureStorageService
|
||||
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
|
||||
|
@ -82,6 +83,7 @@ import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapab
|
|||
import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService
|
||||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
||||
import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
|
||||
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
|
||||
import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor
|
||||
import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor
|
||||
|
@ -373,6 +375,9 @@ internal abstract class SessionModule {
|
|||
@Binds
|
||||
abstract fun bindPermalinkService(service: DefaultPermalinkService): PermalinkService
|
||||
|
||||
@Binds
|
||||
abstract fun bindOpenIdTokenService(service: DefaultOpenIdService): OpenIdService
|
||||
|
||||
@Binds
|
||||
abstract fun bindTypingUsersTracker(tracker: DefaultTypingUsersTracker): TypingUsersTracker
|
||||
|
||||
|
|
|
@ -47,14 +47,15 @@ import java.io.FileNotFoundException
|
|||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class FileUploader @Inject constructor(@Authenticated
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService,
|
||||
private val context: Context,
|
||||
private val temporaryFileCreator: TemporaryFileCreator,
|
||||
contentUrlResolver: ContentUrlResolver,
|
||||
moshi: Moshi) {
|
||||
internal class FileUploader @Inject constructor(
|
||||
@Authenticated private val okHttpClient: OkHttpClient,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService,
|
||||
private val context: Context,
|
||||
private val temporaryFileCreator: TemporaryFileCreator,
|
||||
contentUrlResolver: ContentUrlResolver,
|
||||
moshi: Moshi
|
||||
) {
|
||||
|
||||
private val uploadUrl = contentUrlResolver.uploadUrl
|
||||
private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java)
|
||||
|
@ -120,11 +121,17 @@ internal class FileUploader @Inject constructor(@Authenticated
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun upload(uploadBody: RequestBody, filename: String?, progressListener: ProgressRequestBody.Listener?): ContentUploadResponse {
|
||||
private suspend fun upload(uploadBody: RequestBody,
|
||||
filename: String?,
|
||||
progressListener: ProgressRequestBody.Listener?): ContentUploadResponse {
|
||||
val urlBuilder = uploadUrl.toHttpUrlOrNull()?.newBuilder() ?: throw RuntimeException()
|
||||
|
||||
val httpUrl = urlBuilder
|
||||
.addQueryParameter("filename", filename)
|
||||
.apply {
|
||||
if (filename != null) {
|
||||
addQueryParameter("filename", filename)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
val requestBody = if (progressListener != null) ProgressRequestBody(uploadBody, progressListener) else uploadBody
|
||||
|
|
|
@ -229,7 +229,6 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
val encryptedFile: File?
|
||||
val contentUploadResponse = if (params.isEncrypted) {
|
||||
Timber.v("## Encrypt file")
|
||||
|
||||
encryptedFile = temporaryFileCreator.create()
|
||||
.also { filesToDelete.add(it) }
|
||||
|
||||
|
@ -239,16 +238,22 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
Timber.v("## Uploading file")
|
||||
|
||||
fileUploader
|
||||
.uploadFile(encryptedFile, attachment.name, MimeTypes.OctetStream, progressListener)
|
||||
fileUploader.uploadFile(
|
||||
file = encryptedFile,
|
||||
filename = null,
|
||||
mimeType = MimeTypes.OctetStream,
|
||||
progressListener = progressListener
|
||||
)
|
||||
} else {
|
||||
Timber.v("## Clear file")
|
||||
Timber.v("## Uploading clear file")
|
||||
encryptedFile = null
|
||||
fileUploader
|
||||
.uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener)
|
||||
fileUploader.uploadFile(
|
||||
file = fileToUpload,
|
||||
filename = attachment.name,
|
||||
mimeType = attachment.getSafeMimeType(),
|
||||
progressListener = progressListener
|
||||
)
|
||||
}
|
||||
|
||||
Timber.v("## Update cache storage for ${contentUploadResponse.contentUri}")
|
||||
|
@ -312,7 +317,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream(), thumbnailData.mimeType)
|
||||
val contentUploadResponse = fileUploader.uploadByteArray(
|
||||
byteArray = encryptionResult.encryptedByteArray,
|
||||
filename = "thumb_${params.attachment.name}",
|
||||
filename = null,
|
||||
mimeType = MimeTypes.OctetStream,
|
||||
progressListener = thumbnailProgressListener
|
||||
)
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.identity
|
||||
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdToken
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import org.matrix.android.sdk.internal.session.identity.model.IdentityRegisterResponse
|
||||
import org.matrix.android.sdk.internal.session.openid.RequestOpenIdTokenResponse
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
|
@ -52,5 +52,5 @@ internal interface IdentityAuthAPI {
|
|||
* The request body is the same as the values returned by /openid/request_token in the Client-Server API.
|
||||
*/
|
||||
@POST(NetworkConstants.URI_IDENTITY_PATH_V2 + "account/register")
|
||||
suspend fun register(@Body openIdToken: RequestOpenIdTokenResponse): IdentityRegisterResponse
|
||||
suspend fun register(@Body openIdToken: OpenIdToken): IdentityRegisterResponse
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
|
|||
return withOlmUtility { olmUtility ->
|
||||
threePids.map { threePid ->
|
||||
base64ToBase64Url(
|
||||
olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT)
|
||||
olmUtility.sha256(threePid.value.lowercase(Locale.ROOT)
|
||||
+ " " + threePid.toMedium() + " " + pepper)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,16 +16,16 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.identity
|
||||
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdToken
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.identity.model.IdentityRegisterResponse
|
||||
import org.matrix.android.sdk.internal.session.openid.RequestOpenIdTokenResponse
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface IdentityRegisterTask : Task<IdentityRegisterTask.Params, IdentityRegisterResponse> {
|
||||
data class Params(
|
||||
val identityAuthAPI: IdentityAuthAPI,
|
||||
val openIdTokenResponse: RequestOpenIdTokenResponse
|
||||
val openIdToken: OpenIdToken
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ internal class DefaultIdentityRegisterTask @Inject constructor() : IdentityRegis
|
|||
|
||||
override suspend fun execute(params: IdentityRegisterTask.Params): IdentityRegisterResponse {
|
||||
return executeRequest(null) {
|
||||
params.identityAuthAPI.register(params.openIdTokenResponse)
|
||||
params.identityAuthAPI.register(params.openIdToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.openid
|
||||
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdToken
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultOpenIdService @Inject constructor(private val getOpenIdTokenTask: GetOpenIdTokenTask): OpenIdService {
|
||||
|
||||
override suspend fun getOpenIdToken(): OpenIdToken {
|
||||
return getOpenIdTokenTask.execute(Unit)
|
||||
}
|
||||
}
|
|
@ -16,20 +16,21 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.openid
|
||||
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdToken
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetOpenIdTokenTask : Task<Unit, RequestOpenIdTokenResponse>
|
||||
internal interface GetOpenIdTokenTask : Task<Unit, OpenIdToken>
|
||||
|
||||
internal class DefaultGetOpenIdTokenTask @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val openIdAPI: OpenIdAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver) : GetOpenIdTokenTask {
|
||||
|
||||
override suspend fun execute(params: Unit): RequestOpenIdTokenResponse {
|
||||
override suspend fun execute(params: Unit): OpenIdToken {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
openIdAPI.openIdToken(userId)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.openid
|
||||
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdToken
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import retrofit2.http.Body
|
||||
|
@ -34,5 +35,5 @@ internal interface OpenIdAPI {
|
|||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/openid/request_token")
|
||||
suspend fun openIdToken(@Path("userId") userId: String,
|
||||
@Body body: JsonDict = emptyMap()): RequestOpenIdTokenResponse
|
||||
@Body body: JsonDict = emptyMap()): OpenIdToken
|
||||
}
|
||||
|
|
|
@ -358,10 +358,9 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
if (it != null) addAll(it)
|
||||
}
|
||||
}.distinct()
|
||||
if (flattenRelated.isEmpty()) {
|
||||
dmRoom.flattenParentIds = null
|
||||
} else {
|
||||
dmRoom.flattenParentIds = "|${flattenRelated.joinToString("|")}|"
|
||||
if (flattenRelated.isNotEmpty()) {
|
||||
// we keep real m.child/m.parent relations and add the one for common memberships
|
||||
dmRoom.flattenParentIds += "|${flattenRelated.joinToString("|")}|"
|
||||
}
|
||||
// Timber.v("## SPACES: flatten of ${dmRoom.otherMemberIds.joinToString(",")} is ${dmRoom.flattenParentIds}")
|
||||
}
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.securestorage
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.security.KeyPairGeneratorSpec
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import androidx.annotation.RequiresApi
|
||||
import org.matrix.android.sdk.internal.util.system.BuildVersionSdkIntProvider
|
||||
import timber.log.Timber
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
@ -32,6 +34,7 @@ import java.io.InputStream
|
|||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.math.BigInteger
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.KeyStore
|
||||
|
@ -58,23 +61,19 @@ import javax.security.auth.x500.X500Principal
|
|||
* is not available.
|
||||
*
|
||||
* <b>Android [K-M[</b>
|
||||
* For android >=KITKAT and <M, we use the keystore to generate and store a private/public key pair. Then for each secret, a
|
||||
* For android >=L and <M, we use the keystore to generate and store a private/public key pair. Then for each secret, a
|
||||
* random secret key in generated to perform encryption.
|
||||
* This secret key is encrypted with the public RSA key and stored with the encrypted secret.
|
||||
* In order to decrypt the encrypted secret key will be retrieved then decrypted with the RSA private key.
|
||||
*
|
||||
* <b>Older androids</b>
|
||||
* For older androids as a fallback we generate an AES key from the alias using PBKDF2 with random salt.
|
||||
* The salt and iv are stored with encrypted data.
|
||||
*
|
||||
* Sample usage:
|
||||
* <code>
|
||||
* val secret = "The answer is 42"
|
||||
* val KEncrypted = SecretStoringUtils.securelyStoreString(secret, "myAlias", context)
|
||||
* val KEncrypted = SecretStoringUtils.securelyStoreString(secret, "myAlias")
|
||||
* //This can be stored anywhere e.g. encoded in b64 and stored in preference for example
|
||||
*
|
||||
* //to get back the secret, just call
|
||||
* val kDecrypted = SecretStoringUtils.loadSecureSecret(KEncrypted!!, "myAlias", context)
|
||||
* val kDecrypted = SecretStoringUtils.loadSecureSecret(KEncrypted, "myAlias")
|
||||
* </code>
|
||||
*
|
||||
* You can also just use this utility to store a secret key, and use any encryption algorithm that you want.
|
||||
|
@ -82,7 +81,10 @@ import javax.security.auth.x500.X500Principal
|
|||
* Important: Keys stored in the keystore can be wiped out (depends of the OS version, like for example if you
|
||||
* add a pin or change the schema); So you might and with a useless pile of bytes.
|
||||
*/
|
||||
internal class SecretStoringUtils @Inject constructor(private val context: Context) {
|
||||
internal class SecretStoringUtils @Inject constructor(
|
||||
private val context: Context,
|
||||
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private const val ANDROID_KEY_STORE = "AndroidKeyStore"
|
||||
|
@ -91,7 +93,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
|
||||
private const val FORMAT_API_M: Byte = 0
|
||||
private const val FORMAT_1: Byte = 1
|
||||
private const val FORMAT_2: Byte = 2
|
||||
}
|
||||
|
||||
private val keyStore: KeyStore by lazy {
|
||||
|
@ -113,43 +114,52 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
/**
|
||||
* Encrypt the given secret using the android Keystore.
|
||||
* On android >= M, will directly use the keystore to generate a symmetric key
|
||||
* On android >= KitKat and <M, as symmetric key gen is not available, will use an symmetric key generated
|
||||
* On android >= Lollipop and <M, as symmetric key gen is not available, will use an symmetric key generated
|
||||
* in the keystore to encrypted a random symmetric key. The encrypted symmetric key is returned
|
||||
* in the bytearray (in can be stored anywhere, it is encrypted)
|
||||
* On older version a key in generated from alias with random salt.
|
||||
*
|
||||
* The secret is encrypted using the following method: AES/GCM/NoPadding
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Throws(Exception::class)
|
||||
fun securelyStoreString(secret: String, keyAlias: String): ByteArray? {
|
||||
fun securelyStoreString(secret: String, keyAlias: String): ByteArray {
|
||||
return when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias)
|
||||
else -> encryptString(secret, keyAlias)
|
||||
buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias)
|
||||
else -> encryptString(secret, keyAlias)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a secret that was encrypted by #securelyStoreString()
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Throws(Exception::class)
|
||||
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String? {
|
||||
return when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> decryptStringM(encrypted, keyAlias)
|
||||
else -> decryptString(encrypted, keyAlias)
|
||||
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String {
|
||||
encrypted.inputStream().use { inputStream ->
|
||||
// First get the format
|
||||
return when (val format = inputStream.read().toByte()) {
|
||||
FORMAT_API_M -> decryptStringM(inputStream, keyAlias)
|
||||
FORMAT_1 -> decryptString(inputStream, keyAlias)
|
||||
else -> throw IllegalArgumentException("Unknown format $format")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun securelyStoreObject(any: Any, keyAlias: String, output: OutputStream) {
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> saveSecureObjectM(keyAlias, output, any)
|
||||
else -> saveSecureObject(keyAlias, output, any)
|
||||
buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.M -> saveSecureObjectM(keyAlias, output, any)
|
||||
else -> saveSecureObject(keyAlias, output, any)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T? {
|
||||
return when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> loadSecureObjectM(keyAlias, inputStream)
|
||||
else -> loadSecureObject(keyAlias, inputStream)
|
||||
// First get the format
|
||||
return when (val format = inputStream.read().toByte()) {
|
||||
FORMAT_API_M -> loadSecureObjectM(keyAlias, inputStream)
|
||||
FORMAT_1 -> loadSecureObject(keyAlias, inputStream)
|
||||
else -> throw IllegalArgumentException("Unknown format $format")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +190,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
- Store the encrypted AES
|
||||
Generate a key pair for encryption
|
||||
*/
|
||||
fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry {
|
||||
private fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry {
|
||||
val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry)
|
||||
|
||||
if (privateKeyEntry != null) return privateKeyEntry
|
||||
|
@ -193,7 +203,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
.setAlias(alias)
|
||||
.setSubject(X500Principal("CN=$alias"))
|
||||
.setSerialNumber(BigInteger.TEN)
|
||||
// .setEncryptionRequired() requires that the phone as a pin/schema
|
||||
// .setEncryptionRequired() requires that the phone has a pin/schema
|
||||
.setStartDate(start.time)
|
||||
.setEndDate(end.time)
|
||||
.build()
|
||||
|
@ -205,7 +215,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun encryptStringM(text: String, keyAlias: String): ByteArray? {
|
||||
private fun encryptStringM(text: String, keyAlias: String): ByteArray {
|
||||
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias)
|
||||
|
||||
val cipher = Cipher.getInstance(AES_MODE)
|
||||
|
@ -217,8 +227,8 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
private fun decryptStringM(encryptedChunk: ByteArray, keyAlias: String): String {
|
||||
val (iv, encryptedText) = formatMExtract(encryptedChunk.inputStream())
|
||||
private fun decryptStringM(inputStream: InputStream, keyAlias: String): String {
|
||||
val (iv, encryptedText) = formatMExtract(inputStream)
|
||||
|
||||
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias)
|
||||
|
||||
|
@ -229,7 +239,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
return String(cipher.doFinal(encryptedText), Charsets.UTF_8)
|
||||
}
|
||||
|
||||
private fun encryptString(text: String, keyAlias: String): ByteArray? {
|
||||
private fun encryptString(text: String, keyAlias: String): ByteArray {
|
||||
// we generate a random symmetric key
|
||||
val key = ByteArray(16)
|
||||
secureRandom.nextBytes(key)
|
||||
|
@ -246,8 +256,8 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
return format1Make(encryptedKey, iv, encryptedBytes)
|
||||
}
|
||||
|
||||
private fun decryptString(data: ByteArray, keyAlias: String): String? {
|
||||
val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data))
|
||||
private fun decryptString(inputStream: InputStream, keyAlias: String): String {
|
||||
val (encryptedKey, iv, encrypted) = format1Extract(inputStream)
|
||||
|
||||
// we need to decrypt the key
|
||||
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey))
|
||||
|
@ -307,30 +317,11 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
output.write(bos1.toByteArray())
|
||||
}
|
||||
|
||||
// @RequiresApi(Build.VERSION_CODES.M)
|
||||
// @Throws(IOException::class)
|
||||
// fun saveSecureObjectM(keyAlias: String, file: File, writeObject: Any) {
|
||||
// FileOutputStream(file).use {
|
||||
// saveSecureObjectM(keyAlias, it, writeObject)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @RequiresApi(Build.VERSION_CODES.M)
|
||||
// @Throws(IOException::class)
|
||||
// fun <T> loadSecureObjectM(keyAlias: String, file: File): T? {
|
||||
// FileInputStream(file).use {
|
||||
// return loadSecureObjectM<T>(keyAlias, it)
|
||||
// }
|
||||
// }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
@Throws(IOException::class)
|
||||
private fun <T> loadSecureObjectM(keyAlias: String, inputStream: InputStream): T? {
|
||||
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias)
|
||||
|
||||
val format = inputStream.read()
|
||||
assert(format.toByte() == FORMAT_API_M)
|
||||
|
||||
val ivSize = inputStream.read()
|
||||
val iv = ByteArray(ivSize)
|
||||
inputStream.read(iv, 0, ivSize)
|
||||
|
@ -393,9 +384,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
}
|
||||
|
||||
private fun formatMExtract(bis: InputStream): Pair<ByteArray, ByteArray> {
|
||||
val format = bis.read().toByte()
|
||||
assert(format == FORMAT_API_M)
|
||||
|
||||
val ivSize = bis.read()
|
||||
val iv = ByteArray(ivSize)
|
||||
bis.read(iv, 0, ivSize)
|
||||
|
@ -414,9 +402,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
}
|
||||
|
||||
private fun format1Extract(bis: InputStream): Triple<ByteArray, ByteArray, ByteArray> {
|
||||
val format = bis.read()
|
||||
assert(format.toByte() == FORMAT_1)
|
||||
|
||||
val keySizeBig = bis.read()
|
||||
val keySizeLow = bis.read()
|
||||
val encryptedKeySize = keySizeBig.shl(8) + keySizeLow
|
||||
|
@ -443,32 +428,4 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
|
|||
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
private fun format2Make(salt: ByteArray, iv: ByteArray, encryptedBytes: ByteArray): ByteArray {
|
||||
val bos = ByteArrayOutputStream(3 + salt.size + iv.size + encryptedBytes.size)
|
||||
bos.write(FORMAT_2.toInt())
|
||||
bos.write(salt.size)
|
||||
bos.write(salt)
|
||||
bos.write(iv.size)
|
||||
bos.write(iv)
|
||||
bos.write(encryptedBytes)
|
||||
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
private fun format2Extract(bis: InputStream): Triple<ByteArray, ByteArray, ByteArray> {
|
||||
val format = bis.read()
|
||||
assert(format.toByte() == FORMAT_2)
|
||||
|
||||
val saltSize = bis.read()
|
||||
val salt = ByteArray(saltSize)
|
||||
bis.read(salt)
|
||||
|
||||
val ivSize = bis.read()
|
||||
val iv = ByteArray(ivSize)
|
||||
bis.read(iv)
|
||||
|
||||
val encrypted = bis.readBytes()
|
||||
return Triple(salt, iv, encrypted)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,6 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
} else {
|
||||
this.preset = CreateRoomPreset.PRESET_PRIVATE_CHAT
|
||||
visibility = RoomDirectoryVisibility.PRIVATE
|
||||
enableEncryption()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -146,7 +145,9 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
viaServers = childStateEvContent.via.orEmpty(),
|
||||
activeMemberCount = childSummary.numJoinedMembers,
|
||||
parentRoomId = childStateEv.roomId,
|
||||
suggested = childStateEvContent.suggested
|
||||
suggested = childStateEvContent.suggested,
|
||||
canonicalAlias = childSummary.canonicalAlias,
|
||||
aliases = childSummary.aliases
|
||||
)
|
||||
}
|
||||
}.orEmpty()
|
||||
|
|
|
@ -83,7 +83,7 @@ internal class DefaultJoinSpaceTask @Inject constructor(
|
|||
Timber.v("## Space: > Sync done ...")
|
||||
// after that i should have the children (? do I need to paginate to get state)
|
||||
val summary = roomSummaryDataSource.getSpaceSummary(params.roomIdOrAlias)
|
||||
Timber.v("## Space: Found space summary Name:[${summary?.name}] children: ${summary?.spaceChildren?.size}")
|
||||
Timber.v("## Space: Found space summary Name:[${summary?.name}] children: ${summary?.spaceChildren?.size}")
|
||||
summary?.spaceChildren?.forEach {
|
||||
// val childRoomSummary = it.roomSummary ?: return@forEach
|
||||
Timber.v("## Space: Processing child :[${it.childRoomId}] autoJoin:${it.autoJoin}")
|
||||
|
|
|
@ -64,7 +64,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
|
|||
* @return true if the event has been decrypted
|
||||
*/
|
||||
private fun decryptToDeviceEvent(event: Event, timelineId: String?): Boolean {
|
||||
Timber.v("## CRYPTO | decryptToDeviceEvent")
|
||||
Timber.v("## CRYPTO | decryptToDeviceEvent")
|
||||
if (event.getClearType() == EventType.ENCRYPTED) {
|
||||
var result: MXEventDecryptionResult? = null
|
||||
try {
|
||||
|
@ -76,7 +76,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
|
|||
val deviceId = cryptoService.getCryptoDeviceInfo(event.senderId!!).firstOrNull {
|
||||
it.identityKey() == senderKey
|
||||
}?.deviceId ?: senderKey
|
||||
Timber.e("## CRYPTO | Failed to decrypt to device event from ${event.senderId}|$deviceId reason:<${event.mCryptoError ?: exception}>")
|
||||
Timber.e("## CRYPTO | Failed to decrypt to device event from ${event.senderId}|$deviceId reason:<${event.mCryptoError ?: exception}>")
|
||||
}
|
||||
|
||||
if (null != result) {
|
||||
|
@ -89,7 +89,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
|
|||
return true
|
||||
} else {
|
||||
// should not happen
|
||||
Timber.e("## CRYPTO | ERROR NULL DECRYPTION RESULT from ${event.senderId}")
|
||||
Timber.e("## CRYPTO | ERROR NULL DECRYPTION RESULT from ${event.senderId}")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.matrix.android.sdk.internal.session.widgets
|
||||
|
||||
import org.matrix.android.sdk.internal.session.openid.RequestOpenIdTokenResponse
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdToken
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
|
@ -29,7 +29,7 @@ internal interface WidgetsAPI {
|
|||
* @param body the body content (Ref: https://github.com/matrix-org/matrix-doc/pull/1961)
|
||||
*/
|
||||
@POST("register")
|
||||
suspend fun register(@Body body: RequestOpenIdTokenResponse,
|
||||
suspend fun register(@Body body: OpenIdToken,
|
||||
@Query("v") version: String?): RegisterWidgetResponse
|
||||
|
||||
@GET("account")
|
||||
|
|
|
@ -27,7 +27,7 @@ fun String.md5() = try {
|
|||
digest.update(toByteArray())
|
||||
digest.digest()
|
||||
.joinToString("") { String.format("%02X", it) }
|
||||
.toLowerCase(Locale.ROOT)
|
||||
.lowercase(Locale.ROOT)
|
||||
} catch (exc: Exception) {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.util
|
||||
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Convert a string to an UTF8 String
|
||||
|
@ -24,7 +25,7 @@ import timber.log.Timber
|
|||
* @param s the string to convert
|
||||
* @return the utf-8 string
|
||||
*/
|
||||
fun convertToUTF8(s: String): String {
|
||||
internal fun convertToUTF8(s: String): String {
|
||||
return try {
|
||||
val bytes = s.toByteArray(Charsets.UTF_8)
|
||||
String(bytes)
|
||||
|
@ -40,7 +41,7 @@ fun convertToUTF8(s: String): String {
|
|||
* @param s the string to convert
|
||||
* @return the utf-16 string
|
||||
*/
|
||||
fun convertFromUTF8(s: String): String {
|
||||
internal fun convertFromUTF8(s: String): String {
|
||||
return try {
|
||||
val bytes = s.toByteArray()
|
||||
String(bytes, Charsets.UTF_8)
|
||||
|
@ -56,7 +57,7 @@ fun convertFromUTF8(s: String): String {
|
|||
* @param subString the string to search for
|
||||
* @return whether a match was found
|
||||
*/
|
||||
fun String.caseInsensitiveFind(subString: String): Boolean {
|
||||
internal fun String.caseInsensitiveFind(subString: String): Boolean {
|
||||
// add sanity checks
|
||||
if (subString.isEmpty() || isEmpty()) {
|
||||
return false
|
||||
|
@ -78,3 +79,14 @@ internal val spaceChars = "[\u00A0\u2000-\u200B\u2800\u3000]".toRegex()
|
|||
* Strip all the UTF-8 chars which are actually spaces
|
||||
*/
|
||||
internal fun String.replaceSpaceChars() = replace(spaceChars, "")
|
||||
|
||||
// String.capitalize is now deprecated
|
||||
internal fun String.safeCapitalize(): String {
|
||||
return replaceFirstChar { char ->
|
||||
if (char.isLowerCase()) {
|
||||
char.titlecase(Locale.getDefault())
|
||||
} else {
|
||||
char.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.util.system
|
||||
|
||||
internal interface BuildVersionSdkIntProvider {
|
||||
/**
|
||||
* Return the current version of the Android SDK
|
||||
*/
|
||||
fun get(): Int
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.util.system
|
||||
|
||||
import android.os.Build
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultBuildVersionSdkIntProvider @Inject constructor()
|
||||
: BuildVersionSdkIntProvider {
|
||||
override fun get() = Build.VERSION.SDK_INT
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.util.system
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
||||
@Module
|
||||
internal abstract class SystemModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindBuildVersionSdkIntProvider(provider: DefaultBuildVersionSdkIntProvider): BuildVersionSdkIntProvider
|
||||
}
|
|
@ -42,7 +42,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.3"
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.2'
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ Formatter\.formatShortFileSize===1
|
|||
# android\.text\.TextUtils
|
||||
|
||||
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
|
||||
enum class===100
|
||||
enum class===101
|
||||
|
||||
### Do not import temporary legacy classes
|
||||
import org.matrix.android.sdk.internal.legacy.riot===3
|
||||
|
|
|
@ -17,7 +17,7 @@ PARAM_KEYSTORE_PATH=$1
|
|||
PARAM_APK=$2
|
||||
|
||||
# Other params
|
||||
BUILD_TOOLS_VERSION="29.0.3"
|
||||
BUILD_TOOLS_VERSION="30.0.3"
|
||||
MIN_SDK_VERSION=21
|
||||
|
||||
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."
|
||||
|
|
|
@ -23,7 +23,7 @@ PARAM_KS_PASS=$3
|
|||
PARAM_KEY_PASS=$4
|
||||
|
||||
# Other params
|
||||
BUILD_TOOLS_VERSION="29.0.3"
|
||||
BUILD_TOOLS_VERSION="30.0.3"
|
||||
MIN_SDK_VERSION=21
|
||||
|
||||
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue