Merge branch 'develop' into feature/fga/timeline_chunks_rework

This commit is contained in:
ganfra 2021-09-17 19:40:20 +02:00
commit 63aa5b4015
201 changed files with 3391 additions and 1035 deletions

View File

@ -36,6 +36,7 @@ body:
- [ ] Push `main` and the new tag `v1.1.10` to origin
- [ ] Checkout `develop`
- [ ] Increase version in `./vector/build.gradle`
- [ ] Change the value of SDK_VERSION in the file `./matrix-sdk-android/build.gradle`
- [ ] Commit and push `develop`
- [ ] Wait for [Buildkite](https://buildkite.com/matrix-dot-org/element-android/builds?branch=main) to build the `main` branch.
- [ ] Run the script `~/scripts/releaseElement.sh`. It will download the APKs from Buildkite check them and sign them.
@ -72,9 +73,25 @@ body:
- [ ] Create a release with GitFlow
- [ ] Update the files `./build.gradle` and `./gradle/gradle-wrapper.properties` manually, to use the latest version for the dependency. You can get inspired by the same files on Element Android project.
- [ ] Run the script `./tools/import_from_element.sh`
- [ ] Update the version in `./matrix-sdk-android/build.gradle` and let the script finish to build the library
- [ ] Update the version in `./matrix-sdk-android/build.gradle`
- [ ] Check the diff on this file and restore what may have been erased (in particular the line `apply plugin: "com.vanniktech.maven.publish"`)
- [ ] Let the script finish to build the library
- [ ] Update the file `CHANGES.md`
- [ ] Update the value of VERSION_NAME in the file gradle.properties
- [ ] Finish the release using GitFlow
##### Release on MavenCentral
- [ ] Run the command `./gradlew publish --no-daemon --no-parallel`. You'll need some non-public element to do so
- [ ] Connect to https://s01.oss.sonatype.org
- [ ] Click on Staging Repositories and check the the files have been uploaded
- [ ] Click on close
- [ ] Wait (check Activity tab until step "Repository closed" is displayed)
- [ ] Click on release. The staging repository will disappear
- [ ] Check that the release is available in https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/ (it can take a few minutes)
##### Release on GitHub
- [ ] Create the release on GitHub from [the tag](https://github.com/matrix-org/matrix-android-sdk2/tags)
- [ ] Upload the AAR on the GitHub release
@ -82,7 +99,7 @@ body:
https://github.com/matrix-org/matrix-android-sdk2-sample
- [ ] Update the dependency to the new version of the SDK2. Jitpack will have to build the AAR, it can take a few minutes. You can check status on https://jitpack.io/#matrix-org/matrix-android-sdk2
- [ ] Update the dependency to the new version of the SDK2. It can take some time for MavenCentral to make the librarie available. You can check status on https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/
- [ ] Build and run the sample, you may have to fix some API break
- [ ] Commit and push directly on `main`
validations:

View File

@ -5,6 +5,12 @@ on:
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
CI_GRADLE_ARG_PROPERTIES: >
-Porg.gradle.jvmargs=-Xmx2g
-Porg.gradle.parallel=false
jobs:
debug:
name: Build debug APKs (${{ matrix.target }})
@ -25,7 +31,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Assemble ${{ matrix.target }} debug apk
run: ./gradlew assemble${{ matrix.target }}Debug --stacktrace
run: ./gradlew assemble${{ matrix.target }}Debug $CI_GRADLE_ARG_PROPERTIES --stacktrace
- name: Upload ${{ matrix.target }} debug APKs
uses: actions/upload-artifact@v2
with:
@ -48,7 +54,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Assemble GPlay unsigned apk
run: ./gradlew clean assembleGplayRelease --stacktrace
run: ./gradlew clean assembleGplayRelease $CI_GRADLE_ARG_PROPERTIES --stacktrace
- name: Upload Gplay unsigned APKs
uses: actions/upload-artifact@v2
with:

View File

@ -5,6 +5,12 @@ on:
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
CI_GRADLE_ARG_PROPERTIES: >
-Porg.gradle.jvmargs=-Xmx2g
-Porg.gradle.parallel=false
jobs:
# Temporary add build of Android tests, which cannot be run on the CI right now, but they need to at least compile
# So it will be mandatory for this action to be successful on every PRs
@ -22,7 +28,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Compile Android tests
run: ./gradlew clean assembleAndroidTest --stacktrace -PallWarningsAsErrors=false
run: ./gradlew clean assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
integration-tests:
name: Integration Tests (Synapse)
@ -30,9 +36,14 @@ jobs:
strategy:
fail-fast: false
matrix:
api-level: [21, 28, 30]
api-level: [28]
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 11
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
@ -64,5 +75,12 @@ jobs:
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
#arch: x86_64
#disable-animations: true
# script: ./gradlew -PallWarningsAsErrors=false vector:connectedAndroidTest matrix-sdk-android:connectedAndroidTest
script: ./gradlew -PallWarningsAsErrors=false connectedCheck
arch: x86
profile: Nexus 5X
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
emulator-build: 7425822
script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace

View File

@ -5,6 +5,12 @@ on:
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
CI_GRADLE_ARG_PROPERTIES: >
-Porg.gradle.jvmargs=-Xmx2g
-Porg.gradle.parallel=false
jobs:
integration-tests:
name: Sanity Tests (Synapse)
@ -46,5 +52,5 @@ jobs:
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
script: ./gradlew -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest
script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest

View File

@ -5,6 +5,12 @@ on:
push:
branches: [main, develop]
# Enrich gradle.properties for CI/CD
env:
CI_GRADLE_ARG_PROPERTIES: >
-Porg.gradle.jvmargs=-Xmx2g
-Porg.gradle.parallel=false
jobs:
unit-tests:
name: Run Unit Tests
@ -20,4 +26,9 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Run unit tests
run: ./gradlew clean test --stacktrace -PallWarningsAsErrors=false
run: ./gradlew clean test $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false --stacktrace
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v1
if: always()
with:
files: ./**/build/test-results/**/*.xml

View File

@ -1,3 +1,62 @@
Changes in Element v1.2.2 (2021-09-13)
======================================
Bugfixes 🐛
----------
- Fix a security issue with message key sharing. See https://matrix.org/blog/2021/09/13/vulnerability-disclosure-key-sharing for details.
Changes in Element v1.2.1 (2021-09-08)
======================================
Features ✨
----------
- Support Android 11 Conversation features ([#1809](https://github.com/vector-im/element-android/issues/1809))
- Introduces AutoAcceptInvites which can be enabled at compile time. ([#3531](https://github.com/vector-im/element-android/issues/3531))
- New call designs ([#3599](https://github.com/vector-im/element-android/issues/3599))
- Restricted Join Rule | Inform admins of new option ([#3631](https://github.com/vector-im/element-android/issues/3631))
- Mention and Keyword Notification Settings: Turn on/off keyword notifications and edit keywords. ([#3650](https://github.com/vector-im/element-android/issues/3650))
- Support accept 3pid invite when email is not bound to account ([#3691](https://github.com/vector-im/element-android/issues/3691))
- Space summary pagination ([#3693](https://github.com/vector-im/element-android/issues/3693))
- Update Email invite to be aware of spaces ([#3695](https://github.com/vector-im/element-android/issues/3695))
- M11.12 Spaces | Default to 'Home' in settings ([#3754](https://github.com/vector-im/element-android/issues/3754))
- Call: show dialog for some ended reasons. ([#3853](https://github.com/vector-im/element-android/issues/3853))
- Add expired account error code in the matrix SDK ([#3900](https://github.com/vector-im/element-android/issues/3900))
- Add password errors in the matrix SDK ([#3927](https://github.com/vector-im/element-android/issues/3927))
- Upgrade AGP to 7.0.2.
When compiling using command line, make sure to use the JDK 11 by adding for instance `-Dorg.gradle.java.home=/Applications/Android\ Studio\ Preview.app/Contents/jre/Contents/Home` or by setting JAVA_HOME. ([#3954](https://github.com/vector-im/element-android/issues/3954))
- Check power level before displaying actions in the room details' timeline ([#3959](https://github.com/vector-im/element-android/issues/3959))
Bugfixes 🐛
----------
- Add mxid to autocomplete suggestion if more than one user in a room has the same displayname ([#1823](https://github.com/vector-im/element-android/issues/1823))
- Use WebView cache for widgets to avoid excessive data use ([#2648](https://github.com/vector-im/element-android/issues/2648))
- Jitsi-hosted jitsi conferences not loading ([#2846](https://github.com/vector-im/element-android/issues/2846))
- Space Explore Rooms no feedback on failed to join ([#3207](https://github.com/vector-im/element-android/issues/3207))
- Notifications - Fix missing sound on notifications. ([#3243](https://github.com/vector-im/element-android/issues/3243))
- the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid ([#3735](https://github.com/vector-im/element-android/issues/3735))
- Update the AccountData with the users' matrix Id instead of their email for those invited by email in a direct chat ([#3743](https://github.com/vector-im/element-android/issues/3743))
- Send an empty body for POST rooms/{roomId}/receipt/{receiptType}/{eventId} ([#3789](https://github.com/vector-im/element-android/issues/3789))
- Fix order in which the items of the attachment menu appear ([#3793](https://github.com/vector-im/element-android/issues/3793))
- Authenticated Jitsi not working in release ([#3841](https://github.com/vector-im/element-android/issues/3841))
- Home: Dial pad lost entry when config changes ([#3845](https://github.com/vector-im/element-android/issues/3845))
- Message edition is not rendered in e2e rooms after pagination ([#3887](https://github.com/vector-im/element-android/issues/3887))
- Crash on opening a room on Android 5.0 and 5.1 - Regression with Voice message ([#3897](https://github.com/vector-im/element-android/issues/3897))
- Fix a crash at start-up if translated string is empty ([#3910](https://github.com/vector-im/element-android/issues/3910))
- PushRule enabling request is not following the spec ([#3911](https://github.com/vector-im/element-android/issues/3911))
- Enable image preview in Android's share sheet (Android 11+) ([#3965](https://github.com/vector-im/element-android/issues/3965))
- Voice Message - Cannot render voice message if the waveform data is corrupted ([#3983](https://github.com/vector-im/element-android/issues/3983))
- Fix memory leak on RoomDetailFragment (ValueAnimator) ([#3990](https://github.com/vector-im/element-android/issues/3990))
Other changes
-------------
- VoIP: Merge virtual room timeline in corresponding native room (call events only). ([#3520](https://github.com/vector-im/element-android/issues/3520))
- Issue templates: modernise and sync with element-web ([#3883](https://github.com/vector-im/element-android/issues/3883))
- Issue templates: modernise SDK and release checklists, and add homeserver question for bugs ([#3889](https://github.com/vector-im/element-android/issues/3889))
- Issue templates: merge expected and actual results ([#3960](https://github.com/vector-im/element-android/issues/3960))
Changes in Element v1.2.0 (2021-08-12)
======================================

View File

@ -18,13 +18,12 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 30
compileSdk versions.compileSdk
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
minSdk versions.minSdk
targetSdk versions.targetSdk
}
buildTypes {
@ -34,8 +33,8 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}
kotlinOptions {
jvmTarget = "11"
@ -51,13 +50,13 @@ dependencies {
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 libs.rx.rxKotlin
implementation libs.rx.rxAndroid
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation libs.jetbrains.kotlinStdlib
implementation libs.androidx.core
implementation libs.androidx.appCompat
implementation libs.androidx.recyclerview
implementation 'com.google.android.material:material:1.4.0'
implementation libs.google.material
}

View File

@ -1,9 +1,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
// Ref: https://kotlinlang.org/releases.html
ext.kotlin_version = '1.5.21'
ext.kotlin_coroutines_version = "1.5.0"
apply from: 'dependencies.gradle'
repositories {
google()
jcenter()
@ -11,12 +11,13 @@ buildscript {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
// Release notes of Android Gradle Plugin (AGP):
// https://developer.android.com/studio/releases/gradle-plugin
classpath 'com.android.tools.build:gradle:7.0.2'
classpath libs.gradle.gradlePlugin
classpath libs.gradle.kotlinPlugin
classpath 'com.google.gms:google-services:4.3.10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
classpath "com.likethesalad.android:string-reference:1.2.2"

View File

@ -1 +0,0 @@
Support Android 11 Conversation features

View File

@ -1 +0,0 @@
- Add mxid to autocomplete suggestion if more than one user in a room has the same displayname

View File

@ -1 +0,0 @@
Use WebView cache for widgets to avoid excessive data use

View File

@ -1 +0,0 @@
Jitsi-hosted jitsi conferences not loading

View File

@ -1 +0,0 @@
Space Explore Rooms no feedback on failed to join

View File

@ -1 +0,0 @@
Notifications - Fix missing sound on notifications.

View File

@ -1 +0,0 @@
VoIP: Merge virtual room timeline in corresponding native room (call events only).

View File

@ -1 +0,0 @@
Introduces AutoAcceptInvites which can be enabled at compile time.

View File

@ -1 +0,0 @@
New call designs

View File

@ -1 +0,0 @@
Restricted Join Rule | Inform admins of new option

View File

@ -1 +0,0 @@
Mention and Keyword Notification Settings: Turn on/off keyword notifications and edit keywords.

1
changelog.d/3678.feature Normal file
View File

@ -0,0 +1 @@
Spaces | M3.23 Invite by email in create private space flow

View File

@ -1 +0,0 @@
Support accept 3pid invite when email is not bound to account

1
changelog.d/3692.feature Normal file
View File

@ -0,0 +1 @@
Allow to also leave rooms when leaving a space

View File

@ -1 +0,0 @@
Space summary pagination

View File

@ -1 +0,0 @@
Update Email invite to be aware of spaces

View File

@ -1 +0,0 @@
the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid

View File

@ -1 +0,0 @@
Update the AccountData with the users' matrix Id instead of their email for those invited by email in a direct chat

1
changelog.d/3752.feature Normal file
View File

@ -0,0 +1 @@
Better expose adding spaces as Subspaces

View File

@ -1 +0,0 @@
M11.12 Spaces | Default to 'Home' in settings

View File

@ -1 +0,0 @@
Send an empty body for POST rooms/{roomId}/receipt/{receiptType}/{eventId}

View File

@ -1 +0,0 @@
Fix order in which the items of the attachement menu appear

View File

@ -1 +0,0 @@
Authenticated Jitsi not working in release

View File

@ -1 +0,0 @@
Home: Dial pad lost entry when config changes

View File

@ -1 +0,0 @@
Call: show dialog for some ended reasons.

View File

@ -1 +0,0 @@
Issue templates: modernise and sync with element-web

View File

@ -1 +0,0 @@
Message edition is not rendered in e2e rooms after pagination

View File

@ -1 +0,0 @@
Crash on opening a room on Android 5.0 and 5.1 - Regression with Voice message

View File

@ -1 +0,0 @@
Add expired account error code in the matrix SDK

View File

@ -1 +0,0 @@
Fix a crash at start-up if translated string is empty

View File

@ -1 +0,0 @@
PushRule enabling request is not following the spec

View File

@ -1 +0,0 @@
Add password errors in the matrix SDK

1
changelog.d/3945.bugfix Normal file
View File

@ -0,0 +1 @@
Remove the "Teammate spaces aren't quite ready" bottom sheet

1
changelog.d/3946.bugfix Normal file
View File

@ -0,0 +1 @@
Restricted Room previews aren't working

1
changelog.d/3947.bugfix Normal file
View File

@ -0,0 +1 @@
A removed room from a space can't be re-added as it won't be shown in add-room

1
changelog.d/3951.bugfix Normal file
View File

@ -0,0 +1 @@
"Non-Admin" user able to invite others to Private Space (by default)

View File

@ -1,2 +0,0 @@
Upgrade AGP to 7.0.2.
When compiling using command line, make sure to use the JDK 11 by adding for instance `-Dorg.gradle.java.home=/Applications/Android\ Studio\ Preview.app/Contents/jre/Contents/Home` or by setting JAVA_HOME.

1
changelog.d/3956.bugfix Normal file
View File

@ -0,0 +1 @@
Kick user dialog for spaces talks about rooms

View File

@ -1 +0,0 @@
Check power level before displaying actions in the room details' timeline

View File

@ -1 +0,0 @@
Issue templates: merge expected and actual results

View File

@ -1 +0,0 @@
Enable image preview in Android's share sheet (Android 11+)

View File

@ -1 +0,0 @@
Voice Message - Cannot render voice message if the waveform data is corrupted

View File

@ -1 +0,0 @@
- Fix memory leak on RoomDetailFragment (ValueAnimator)

1
changelog.d/3994.misc Normal file
View File

@ -0,0 +1 @@
Better support for Sdk2 version. Also slight change in the default user agent: `MatrixAndroidSDK_X` is replaced by `MatrixAndroidSdk2`

1
changelog.d/4014.misc Normal file
View File

@ -0,0 +1 @@
Introduces ConferenceEvent to abstract usage of Jitsi BroadcastEvent class.

1
changelog.d/4015.bugfix Normal file
View File

@ -0,0 +1 @@
Fix DTMF not working

View File

@ -1 +0,0 @@
Issue templates: modernise SDK and release checklists, and add homeserver question for bugs

127
dependencies.gradle Normal file
View File

@ -0,0 +1,127 @@
ext.versions = [
'minSdk' : 21,
'compileSdk' : 30,
'targetSdk' : 30,
'sourceCompat' : JavaVersion.VERSION_11,
'targetCompat' : JavaVersion.VERSION_11,
]
// Ref: https://kotlinlang.org/releases.html
def gradle = "7.0.2"
def kotlin = "1.5.30"
def kotlinCoroutines = "1.5.1"
def dagger = "2.38.1"
def retrofit = "2.9.0"
def arrow = "0.8.2"
def markwon = "4.6.2"
def moshi = "1.12.0"
def lifecycle = "2.2.0"
def rxBinding = "3.1.0"
def epoxy = "4.6.2"
def glide = "4.12.0"
def bigImageViewer = "1.8.1"
def jjwt = "0.11.2"
// Testing
def mockk = "1.12.0"
def espresso = "3.4.0"
def androidxTest = "1.4.0"
ext.libs = [
gradle : [
'gradlePlugin' : "com.android.tools.build:gradle:$gradle",
'kotlinPlugin' : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin"
],
jetbrains : [
'kotlinStdlibJdk7' : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin",
'kotlinStdlib' : "org.jetbrains.kotlin:kotlin-stdlib:$kotlin",
'coroutinesCore' : "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutines",
'coroutinesAndroid' : "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutines",
'coroutinesRx2' : "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutines"
],
androidx : [
'appCompat' : "androidx.appcompat:appcompat:1.3.1",
'core' : "androidx.core:core-ktx:1.6.0",
'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1",
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3",
'fragmentKtx' : "androidx.fragment:fragment-ktx:1.3.6",
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.0",
'work' : "androidx.work:work-runtime-ktx:2.5.0",
'autoFill' : "androidx.autofill:autofill:1.1.0",
'preferenceKtx' : "androidx.preference:preference-ktx:1.1.1",
'junit' : "androidx.test.ext:junit:1.1.3",
'lifecycleExtensions' : "androidx.lifecycle:lifecycle-extensions:$lifecycle",
'lifecycleJava8' : "androidx.lifecycle:lifecycle-common-java8:$lifecycle",
'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1",
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
'coreTesting' : "androidx.arch.core:core-testing:2.1.0",
'testCore' : "androidx.test:core:$androidxTest",
'testRunner' : "androidx.test:runner:$androidxTest",
'testRules' : "androidx.test:rules:$androidxTest",
'espressoCore' : "androidx.test.espresso:espresso-core:$espresso",
'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso",
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso"
],
google : [
'material' : "com.google.android.material:material:1.4.0"
],
dagger : [
'dagger' : "com.google.dagger:dagger:$dagger",
'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger"
],
squareup : [
'moshi' : "com.squareup.moshi:moshi-adapters:$moshi",
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
],
rx : [
'rxKotlin' : "io.reactivex.rxjava2:rxkotlin:2.4.0",
'rxAndroid' : "io.reactivex.rxjava2:rxandroid:2.1.1"
],
arrow : [
'core' : "io.arrow-kt:arrow-core:$arrow",
'instances' : "io.arrow-kt:arrow-instances-core:$arrow"
],
markwon : [
'core' : "io.noties.markwon:core:$markwon",
'html' : "io.noties.markwon:html:$markwon"
],
airbnb : [
'epoxy' : "com.airbnb.android:epoxy:$epoxy",
'epoxyGlide' : "com.airbnb.android:epoxy-glide-preloading:$epoxy",
'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy",
'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy",
'mvrx' : "com.airbnb.android:mvrx:1.5.1"
],
mockk : [
'mockk' : "io.mockk:mockk:$mockk",
'mockkAndroid' : "io.mockk:mockk-android:$mockk"
],
github : [
'glide' : "com.github.bumptech.glide:glide:$glide",
'glideCompiler' : "com.github.bumptech.glide:compiler:$glide",
'bigImageViewer' : "com.github.piasy:BigImageViewer:$bigImageViewer",
'glideImageLoader' : "com.github.piasy:GlideImageLoader:$bigImageViewer",
'progressPieIndicator' : "com.github.piasy:ProgressPieIndicator:$bigImageViewer",
'glideImageViewFactory' : "com.github.piasy:GlideImageViewFactory:$bigImageViewer"
],
jakewharton : [
'timber' : "com.jakewharton.timber:timber:5.0.1",
'rxbinding' : "com.jakewharton.rxbinding3:rxbinding:$rxBinding",
'rxbindingAppcompat' : "com.jakewharton.rxbinding3:rxbinding-appcompat:$rxBinding",
'rxbindingMaterial' : "com.jakewharton.rxbinding3:rxbinding-material:$rxBinding"
],
jsonwebtoken: [
'jjwtApi' : "io.jsonwebtoken:jjwt-api:$jjwt",
'jjwtImpl' : "io.jsonwebtoken:jjwt-impl:$jjwt",
'jjwtOrgjson' : "io.jsonwebtoken:jjwt-orgjson:$jjwt"
],
tests : [
'kluent' : "org.amshove.kluent:kluent-android:1.68",
'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1",
'junit' : "junit:junit:4.13.2"
]
]

View File

@ -1,2 +1,2 @@
Main changes in this version: Voice Message is enabled by default.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.16
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.2.0

View File

@ -0,0 +1,2 @@
Main changes in this version: Many improvements on VoIP and Spaces (still in beta).
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.2.1

View File

@ -6,20 +6,20 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Enable file system watch (https://docs.gradle.org/6.7/release-notes.html)
# Build Time Optimizations
org.gradle.jvmargs=-Xmx3g -Xms512M -XX:MaxPermSize=2048m -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.vfs.watch=true
# Android Settings
android.enableJetifier=true
android.useAndroidX=true
#Project Settings
# Change debugPrivateData to true for debugging
vector.debugPrivateData=false
# httpLogLevel values: NONE, BASIC, HEADERS, BODY
vector.httpLogLevel=BASIC
# Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above
#vector.debugPrivateData=true
#vector.httpLogLevel=BODY

View File

@ -20,14 +20,11 @@ plugins {
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
compileSdk versions.compileSdk
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
minSdk versions.minSdk
targetSdk versions.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
@ -41,8 +38,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}
kotlinOptions {
@ -55,10 +52,10 @@ android {
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation libs.androidx.appCompat
implementation libs.google.material
// Pref theme
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation libs.androidx.preferenceKtx
// PFLockScreen attrs
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
// dialpad dimen

View File

@ -8,6 +8,7 @@
<attr name="actionDescription" format="string" />
<attr name="leftIcon" format="reference" />
<attr name="rightIcon" format="reference" />
<attr name="betaAction" format="boolean" />
<attr name="forceStartPadding" format="boolean" />
</declare-styleable>

View File

@ -3,13 +3,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 30
compileSdk versions.compileSdk
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
minSdk versions.minSdk
targetSdk versions.targetSdk
// Multidex is useful for tests
multiDexEnabled true
@ -24,8 +22,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}
kotlinOptions {
@ -34,15 +32,16 @@ android {
}
dependencies {
implementation project(":matrix-sdk-android")
implementation 'androidx.appcompat:appcompat:1.3.1'
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"
implementation libs.androidx.appCompat
implementation libs.rx.rxKotlin
implementation libs.rx.rxAndroid
implementation libs.jetbrains.coroutinesRx2
// Paging
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
implementation libs.androidx.pagingRuntimeKtx
// Logging
implementation 'com.jakewharton.timber:timber:5.0.1'
implementation libs.jakewharton.timber
}

View File

@ -14,14 +14,14 @@ buildscript {
}
android {
compileSdkVersion 30
testOptions.unitTests.includeAndroidResources = true
compileSdk versions.compileSdk
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "0.0.1"
minSdk versions.minSdk
targetSdk versions.targetSdk
// Multidex is useful for tests
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -31,9 +31,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
// Seems that the build tools 4.1.0 does not generate BuildConfig.VERSION_NAME anymore.
// Add it manually here. We may remove this trick in the future
buildConfigField "String", "VERSION_NAME", "\"0.0.1\""
buildConfigField "String", "SDK_VERSION", "\"1.2.2\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
@ -68,8 +66,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}
kotlinOptions {
@ -103,92 +101,83 @@ static def gitRevisionDate() {
dependencies {
def arrow_version = "0.8.2"
def moshi_version = '1.12.0'
def lifecycle_version = '2.2.0'
def arch_version = '2.1.0'
def markwon_version = '3.1.0'
def daggerVersion = '2.38.1'
def work_version = '2.5.0'
def retrofit_version = '2.9.0'
implementation libs.jetbrains.kotlinStdlibJdk7
implementation libs.jetbrains.coroutinesCore
implementation libs.jetbrains.coroutinesAndroid
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation libs.androidx.appCompat
implementation libs.androidx.core
implementation "androidx.appcompat:appcompat:1.3.1"
implementation "androidx.core:core-ktx:1.6.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation libs.androidx.lifecycleExtensions
implementation libs.androidx.lifecycleJava8
// Network
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
implementation libs.squareup.retrofit
implementation libs.squareup.retrofitMoshi
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))
implementation 'com.squareup.okhttp3:okhttp'
implementation 'com.squareup.okhttp3:logging-interceptor'
implementation 'com.squareup.okhttp3:okhttp-urlconnection'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
implementation libs.squareup.moshi
kapt libs.squareup.moshiKotlin
implementation "ru.noties.markwon:core:$markwon_version"
implementation libs.markwon.core
// Image
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation libs.androidx.exifinterface
// Database
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
kapt 'dk.ilios:realmfieldnameshelper:2.0.0'
// Work
implementation "androidx.work:work-runtime-ktx:$work_version"
implementation libs.androidx.work
// FP
implementation "io.arrow-kt:arrow-core:$arrow_version"
implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
implementation libs.arrow.core
implementation libs.arrow.instances
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
implementation 'org.matrix.gitlab.matrix-org:olm:3.2.4'
// DI
implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
implementation libs.dagger.dagger
kapt libs.dagger.daggerCompiler
// Logging
implementation 'com.jakewharton.timber:timber:5.0.1'
implementation libs.jakewharton.timber
implementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
// Video compression
implementation 'com.otaliastudios:transcoder:0.10.3'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.32'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.5.1'
testImplementation libs.tests.junit
testImplementation 'org.robolectric:robolectric:4.6.1'
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
testImplementation 'io.mockk:mockk:1.12.0'
testImplementation 'org.amshove.kluent:kluent-android:1.68'
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
testImplementation libs.mockk.mockk
testImplementation libs.tests.kluent
implementation libs.jetbrains.coroutinesAndroid
// Plant Timber tree for test
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
androidTestImplementation 'androidx.test:core:1.4.0'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'org.amshove.kluent:kluent-android:1.68'
androidTestImplementation 'io.mockk:mockk-android:1.12.0'
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
kaptAndroidTest libs.dagger.daggerCompiler
androidTestImplementation libs.androidx.testCore
androidTestImplementation libs.androidx.testRunner
androidTestImplementation libs.androidx.testRules
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espressoCore
androidTestImplementation libs.tests.kluent
androidTestImplementation libs.mockk.mockkAndroid
androidTestImplementation libs.androidx.coreTesting
androidTestImplementation libs.jetbrains.coroutinesAndroid
// Plant Timber tree for test
androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
androidTestImplementation libs.tests.timberJunitRule
androidTestUtil 'androidx.test:orchestrator:1.4.0'
}

View File

@ -117,7 +117,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
}
fun getSdkVersion(): String {
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"
}
}
}

View File

@ -19,6 +19,8 @@ package org.matrix.android.sdk.common
import android.content.Context
import android.net.Uri
import androidx.lifecycle.Observer
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
@ -59,13 +61,15 @@ class CommonTestHelper(context: Context) {
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
init {
Matrix.initialize(
context,
MatrixConfiguration(
applicationFlavor = "TestFlavor",
roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider()
)
)
UiThreadStatement.runOnUiThread {
Matrix.initialize(
context,
MatrixConfiguration(
applicationFlavor = "TestFlavor",
roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider()
)
)
}
matrix = Matrix.getInstance(context)
}

View File

@ -32,10 +32,19 @@ import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.powerlevels.Role
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.SessionTestParams
@ -386,6 +395,8 @@ class SpaceHierarchyTest : InstrumentedTest {
// The room should have disapear from flat children
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
}
commonTestHelper.signOutAndClose(session)
}
data class TestSpaceCreationResult(
@ -434,6 +445,57 @@ class SpaceHierarchyTest : InstrumentedTest {
return TestSpaceCreationResult(spaceId, roomIds)
}
@Suppress("EXPERIMENTAL_API_USAGE")
private fun createPrivateSpace(session: Session,
spaceName: String,
childInfo: List<Triple<String, Boolean, Boolean?>>
/** Name, auto-join, canonical*/
): TestSpaceCreationResult {
var spaceId = ""
commonTestHelper.waitWithLatch {
GlobalScope.launch {
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
it.countDown()
}
}
val syncedSpace = session.spaceService().getSpace(spaceId)
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
val roomIds =
childInfo.map { entry ->
var roomId = ""
commonTestHelper.waitWithLatch {
GlobalScope.launch {
val homeServerCapabilities = session
.getHomeServerCapabilities()
roomId = session.createRoom(CreateRoomParams().apply {
name = entry.first
this.featurePreset = RestrictedRoomPreset(
homeServerCapabilities,
listOf(
RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
)
)
})
it.countDown()
}
}
roomId
}
roomIds.forEachIndexed { index, roomId ->
runBlocking {
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
val canonical = childInfo[index].third
if (canonical != null) {
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
}
}
}
return TestSpaceCreationResult(spaceId, roomIds)
}
@Test
fun testRootSpaces() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@ -473,5 +535,111 @@ class SpaceHierarchyTest : InstrumentedTest {
val rootSpaces = session.spaceService().getRootSpaceSummaries()
assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size)
commonTestHelper.signOutAndClose(session)
}
@Test
fun testParentRelation() {
val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true))
val spaceAInfo = createPrivateSpace(aliceSession, "Private Space A", listOf(
Triple("General", true /*suggested*/, true/*canonical*/),
Triple("Random", true, true)
))
commonTestHelper.runBlockingTest {
aliceSession.getRoom(spaceAInfo.spaceId)!!.invite(bobSession.myUserId, null)
}
commonTestHelper.runBlockingTest {
bobSession.joinRoom(spaceAInfo.spaceId, null, emptyList())
}
var bobRoomId = ""
commonTestHelper.waitWithLatch {
GlobalScope.launch {
bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
it.countDown()
}
}
commonTestHelper.runBlockingTest {
aliceSession.joinRoom(bobRoomId)
}
commonTestHelper.waitWithLatch { latch ->
commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession.getRoomSummary(bobRoomId)?.membership?.isActive() == true
}
}
commonTestHelper.waitWithLatch {
GlobalScope.launch {
bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
it.countDown()
}
}
commonTestHelper.waitWithLatch { latch ->
commonTestHelper.retryPeriodicallyWithLatch(latch) {
val stateEvent = aliceSession.getRoom(bobRoomId)!!.getStateEvent(EventType.STATE_SPACE_PARENT, QueryStringValue.Equals(spaceAInfo.spaceId))
stateEvent != null
}
}
// This should be an invalid space parent relation, because no opposite child and bob is not admin of the space
commonTestHelper.runBlockingTest {
// we can see the state event
// but it is not valid and room is not in hierarchy
assertTrue("Bob Room should not be listed as a child of the space", aliceSession.getRoomSummary(bobRoomId)?.flattenParentIds?.isEmpty() == true)
}
// Let's now try to make alice admin of the room
commonTestHelper.waitWithLatch {
GlobalScope.launch {
val room = bobSession.getRoom(bobRoomId)!!
val currentPLContent = room
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
?.let { it.content.toModel<PowerLevelsContent>() }
val newPowerLevelsContent = currentPLContent
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
?.toContent()
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
it.countDown()
}
}
commonTestHelper.waitWithLatch { latch ->
commonTestHelper.retryPeriodicallyWithLatch(latch) {
val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!!
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
?.content
?.toModel<PowerLevelsContent>()
?.let { PowerLevelsHelper(it) }
powerLevelsHelper!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT)
}
}
commonTestHelper.waitWithLatch {
GlobalScope.launch {
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
it.countDown()
}
}
commonTestHelper.waitWithLatch { latch ->
commonTestHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoomSummary(bobRoomId)?.flattenParentIds?.contains(spaceAInfo.spaceId) == true
}
}
commonTestHelper.signOutAndClose(aliceSession)
commonTestHelper.signOutAndClose(bobSession)
}
}

View File

@ -111,7 +111,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
}
fun getSdkVersion(): String {
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"
}
}
}

View File

@ -239,7 +239,7 @@ data class Event(
fun Event.isTextMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_TEXT,
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_NOTICE -> true
@ -249,7 +249,7 @@ fun Event.isTextMessage(): Boolean {
fun Event.isImageMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_IMAGE -> true
else -> false
}
@ -257,7 +257,7 @@ fun Event.isImageMessage(): Boolean {
fun Event.isVideoMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_VIDEO -> true
else -> false
}
@ -265,7 +265,7 @@ fun Event.isVideoMessage(): Boolean {
fun Event.isAudioMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_AUDIO -> true
else -> false
}
@ -273,7 +273,7 @@ fun Event.isAudioMessage(): Boolean {
fun Event.isFileMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_FILE -> true
else -> false
}
@ -281,7 +281,7 @@ fun Event.isFileMessage(): Boolean {
fun Event.isAttachmentMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_IMAGE,
MessageType.MSGTYPE_AUDIO,
MessageType.MSGTYPE_VIDEO,

View File

@ -0,0 +1,111 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* These are the same fields as those returned by /publicRooms, with a few additions: room_type, membership and is_encrypted.
*/
@JsonClass(generateAdapter = true)
data class RoomStrippedState(
/**
* Aliases of the room. May be empty.
*/
@Json(name = "aliases")
val aliases: List<String>? = null,
/**
* The canonical alias of the room, if any.
*/
@Json(name = "canonical_alias")
val canonicalAlias: String? = null,
/**
* The name of the room, if any.
*/
@Json(name = "name")
val name: String? = null,
/**
* Required. The number of members joined to the room.
*/
@Json(name = "num_joined_members")
val numJoinedMembers: Int = 0,
/**
* Required. The ID of the room.
*/
@Json(name = "room_id")
val roomId: String,
/**
* The topic of the room, if any.
*/
@Json(name = "topic")
val topic: String? = null,
/**
* Required. Whether the room may be viewed by guest users without joining.
*/
@Json(name = "world_readable")
val worldReadable: Boolean = false,
/**
* Required. Whether guest users may join the room and participate in it. If they can,
* they will be subject to ordinary power level rules like any other user.
*/
@Json(name = "guest_can_join")
val guestCanJoin: Boolean = false,
/**
* The URL for the room's avatar, if one is set.
*/
@Json(name = "avatar_url")
val avatarUrl: String? = null,
/**
* Undocumented item
*/
@Json(name = "m.federate")
val isFederated: Boolean = false,
/**
* Optional. If the room is encrypted. This is already accessible as stripped state.
*/
@Json(name = "is_encrypted")
val isEncrypted: Boolean?,
/**
* Optional. Type of the room, if any, i.e. m.space
*/
@Json(name = "room_type")
val roomType: String?,
/**
* The current membership of this user in the room. Usually leave if the room is fetched over federation.
*/
@Json(name = "membership")
val membership: String?
) {
/**
* Return the canonical alias, or the first alias from the list of aliases, or null
*/
fun getPrimaryAlias(): String? {
return canonicalAlias ?: aliases?.firstOrNull()
}
}

View File

@ -28,7 +28,7 @@ data class MessageAudioContent(
/**
* Required. Must be 'm.audio'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A description of the audio e.g. 'Bee Gees - Stayin' Alive', or some kind of content description for accessibility e.g. 'audio attachment'.

View File

@ -20,6 +20,11 @@ import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
interface MessageContent {
companion object {
const val MSG_TYPE_JSON_KEY = "msgtype"
}
val msgType: String
val body: String
val relatesTo: RelationDefaultContent?

View File

@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
@JsonClass(generateAdapter = true)
data class MessageDefaultContent(
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
@Json(name = "body") override val body: String,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null

View File

@ -26,7 +26,7 @@ data class MessageEmoteContent(
/**
* Required. Must be 'm.emote'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. The emote action to perform.

View File

@ -28,7 +28,7 @@ data class MessageFileContent(
/**
* Required. Must be 'm.file'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A human-readable description of the file. This is recommended to be the filename of the original upload.

View File

@ -27,7 +27,7 @@ data class MessageImageContent(
/**
* Required. Must be 'm.image'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A textual representation of the image. This could be the alt text of the image, the filename of the image,

View File

@ -26,7 +26,7 @@ data class MessageLocationContent(
/**
* Required. Must be 'm.location'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A description of the location e.g. 'Big Ben, London, UK', or some kind

View File

@ -26,7 +26,7 @@ data class MessageNoticeContent(
/**
* Required. Must be 'm.notice'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. The notice text to send.

View File

@ -30,7 +30,7 @@ const val OPTION_TYPE_BUTTONS = "org.matrix.buttons"
*/
@JsonClass(generateAdapter = true)
data class MessageOptionsContent(
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_OPTIONS,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_OPTIONS,
@Json(name = "type") val optionType: String? = null,
@Json(name = "body") override val body: String,
@Json(name = "label") val label: String?,

View File

@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
*/
@JsonClass(generateAdapter = true)
data class MessagePollResponseContent(
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_RESPONSE,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE,
@Json(name = "body") override val body: String,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null

View File

@ -26,7 +26,7 @@ data class MessageTextContent(
/**
* Required. Must be 'm.text'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. The body of the message.

View File

@ -24,7 +24,7 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationInfoReque
@JsonClass(generateAdapter = true)
data class MessageVerificationRequestContent(
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST,
@Json(name = "body") override val body: String,
@Json(name = "from_device") override val fromDevice: String?,
@Json(name = "methods") override val methods: List<String>,

View File

@ -27,7 +27,7 @@ data class MessageVideoContent(
/**
* Required. Must be 'm.video'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String,
/**
* Required. A description of the video e.g. 'Gangnam style', or some kind of content description for accessibility e.g. 'video attachment'.

View File

@ -94,5 +94,7 @@ interface SpaceService {
*/
suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List<String>)
suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String)
fun getRootSpaceSummaries(): List<RoomSummary>
}

View File

@ -170,7 +170,7 @@ internal class MXMegolmEncryption(
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
for (deviceId in deviceIds!!) {
val deviceInfo = devicesInRoom.getObject(userId, deviceId)
if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, userId, deviceId).found) {
if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, deviceInfo).found) {
val devices = shareMap.getOrPut(userId) { ArrayList() }
devices.add(deviceInfo)
}
@ -270,8 +270,8 @@ internal class MXMegolmEncryption(
// for dead devices on every message.
val gossipingEventBuffer = arrayListOf<Event>()
for ((userId, devicesToShareWith) in devicesByUser) {
for ((deviceId) in devicesToShareWith) {
session.sharedWithHelper.markedSessionAsShared(userId, deviceId, chainIndex)
for (deviceInfo in devicesToShareWith) {
session.sharedWithHelper.markedSessionAsShared(deviceInfo, chainIndex)
gossipingEventBuffer.add(
Event(
type = EventType.ROOM_KEY,
@ -279,7 +279,7 @@ internal class MXMegolmEncryption(
content = submap.apply {
this["session_key"] = ""
// we add a fake key for trail
this["_dest"] = "$userId|$deviceId"
this["_dest"] = "$userId|${deviceInfo.deviceId}"
}
))
}
@ -429,7 +429,7 @@ internal class MXMegolmEncryption(
.also { Timber.w("## Crypto reshareKey: Device not found") }
// Get the chain index of the key we previously sent this device
val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, userId, deviceId)
val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, deviceInfo)
if (!wasSessionSharedWithUser.found) {
// This session was never shared with this user
// Send a room key with held

View File

@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.crypto.algorithms.megolm
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
@ -28,7 +29,13 @@ internal class SharedWithHelper(
return cryptoStore.getSharedWithInfo(roomId, sessionId)
}
fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) {
cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex)
fun markedSessionAsShared(deviceInfo: CryptoDeviceInfo, chainIndex: Int) {
cryptoStore.markedSessionAsShared(
roomId = roomId,
sessionId = sessionId,
userId = deviceInfo.userId,
deviceId = deviceInfo.deviceId,
deviceIdentityKey = deviceInfo.identityKey() ?: "",
chainIndex = chainIndex)
}
}

View File

@ -450,7 +450,8 @@ internal interface IMXCryptoStore {
fun addWithHeldMegolmSession(withHeldContent: RoomKeyWithHeldContent)
fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent?
fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int)
fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String,
deviceIdentityKey: String, chainIndex: Int)
/**
* Query for information on this session sharing history.
@ -459,7 +460,7 @@ internal interface IMXCryptoStore {
* in this case chainIndex is not nullindicates the ratchet position.
* In found is false, chainIndex is null
*/
fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult
fun getSharedSessionInfo(roomId: String?, sessionId: String, deviceInfo: CryptoDeviceInfo): SharedSessionResult
data class SharedSessionResult(val found: Boolean, val chainIndex: Int?)
fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int>

View File

@ -1681,7 +1681,12 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) {
override fun markedSessionAsShared(roomId: String?,
sessionId: String,
userId: String,
deviceId: String,
deviceIdentityKey: String,
chainIndex: Int) {
doRealmTransaction(realmConfiguration) { realm ->
SharedSessionEntity.create(
realm = realm,
@ -1689,14 +1694,22 @@ internal class RealmCryptoStore @Inject constructor(
sessionId = sessionId,
userId = userId,
deviceId = deviceId,
deviceIdentityKey = deviceIdentityKey,
chainIndex = chainIndex
)
}
}
override fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult {
override fun getSharedSessionInfo(roomId: String?, sessionId: String, deviceInfo: CryptoDeviceInfo): IMXCryptoStore.SharedSessionResult {
return doWithRealm(realmConfiguration) { realm ->
SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let {
SharedSessionEntity.get(
realm = realm,
roomId = roomId,
sessionId = sessionId,
userId = deviceInfo.userId,
deviceId = deviceInfo.deviceId,
deviceIdentityKey = deviceInfo.identityKey()
)?.let {
IMXCryptoStore.SharedSessionResult(true, it.chainIndex)
} ?: IMXCryptoStore.SharedSessionResult(false, null)
}

View File

@ -55,7 +55,7 @@ internal object RealmCryptoStoreMigration : RealmMigration {
// 0, 1, 2: legacy Riot-Android
// 3: migrate to RiotX schema
// 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6)
const val CRYPTO_STORE_SCHEMA_VERSION = 13L
const val CRYPTO_STORE_SCHEMA_VERSION = 14L
private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema {
if (!hasField(fieldName)) {
@ -94,6 +94,7 @@ internal object RealmCryptoStoreMigration : RealmMigration {
if (oldVersion <= 10) migrateTo11(realm)
if (oldVersion <= 11) migrateTo12(realm)
if (oldVersion <= 12) migrateTo13(realm)
if (oldVersion <= 13) migrateTo14(realm)
}
private fun migrateTo1Legacy(realm: DynamicRealm) {
@ -554,4 +555,21 @@ internal object RealmCryptoStoreMigration : RealmMigration {
Timber.e("TrustLevelEntity cleanup: Something is not correct...")
}
}
// Version 14L Update the way we remember key sharing
private fun migrateTo14(realm: DynamicRealm) {
Timber.d("Step 13 -> 14")
realm.schema.get("SharedSessionEntity")
?.addField(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, String::class.java)
?.addIndex(SharedSessionEntityFields.DEVICE_IDENTITY_KEY)
?.transform {
val sharedUserId = it.getString(SharedSessionEntityFields.USER_ID)
val sharedDeviceId = it.getString(SharedSessionEntityFields.DEVICE_ID)
val knownDevice = realm.where("DeviceInfoEntity")
.equalTo(DeviceInfoEntityFields.USER_ID, sharedUserId)
.equalTo(DeviceInfoEntityFields.DEVICE_ID, sharedDeviceId)
.findFirst()
it.setString(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, knownDevice?.getString(DeviceInfoEntityFields.IDENTITY_KEY))
}
}
}

View File

@ -30,6 +30,7 @@ internal open class SharedSessionEntity(
@Index var sessionId: String? = null,
@Index var userId: String? = null,
@Index var deviceId: String? = null,
@Index var deviceIdentityKey: String? = null,
var chainIndex: Int? = null
) : RealmObject() {

View File

@ -16,15 +16,20 @@
package org.matrix.android.sdk.internal.crypto.store.db.query
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields
import io.realm.Realm
import io.realm.RealmResults
import io.realm.kotlin.createObject
import io.realm.kotlin.where
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields
internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, sessionId: String, userId: String, deviceId: String)
internal fun SharedSessionEntity.Companion.get(realm: Realm,
roomId: String?,
sessionId: String,
userId: String,
deviceId: String,
deviceIdentityKey: String?)
: SharedSessionEntity? {
return realm.where<SharedSessionEntity>()
.equalTo(SharedSessionEntityFields.ROOM_ID, roomId)
@ -32,6 +37,7 @@ internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, se
.equalTo(SharedSessionEntityFields.ALGORITHM, MXCRYPTO_ALGORITHM_MEGOLM)
.equalTo(SharedSessionEntityFields.USER_ID, userId)
.equalTo(SharedSessionEntityFields.DEVICE_ID, deviceId)
.equalTo(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, deviceIdentityKey)
.findFirst()
}
@ -44,7 +50,12 @@ internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, se
.findAll()
}
internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int)
internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?,
sessionId: String,
userId: String,
deviceId: String,
deviceIdentityKey: String,
chainIndex: Int)
: SharedSessionEntity {
return realm.createObject<SharedSessionEntity>().apply {
this.roomId = roomId
@ -52,6 +63,7 @@ internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?,
this.sessionId = sessionId
this.userId = userId
this.deviceId = deviceId
this.deviceIdentityKey = deviceIdentityKey
this.chainIndex = chainIndex
}
}

View File

@ -36,7 +36,7 @@ internal class UserAgentHolder @Inject constructor(private val context: Context,
/**
* Create an user agent with the application version.
* Ex: Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSDK_X 1.0)
* Ex: Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0)
*
* @param flavorDescription the flavor description
*/
@ -74,13 +74,13 @@ internal class UserAgentHolder @Inject constructor(private val context: Context,
// if there is no user agent or cannot parse it
if (null == systemUserAgent || systemUserAgent.lastIndexOf(")") == -1 || !systemUserAgent.contains("(")) {
userAgent = (appName + "/" + appVersion + " ( Flavour " + flavorDescription
+ "; MatrixAndroidSDK_X " + BuildConfig.VERSION_NAME + ")")
+ "; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")")
} else {
// update
userAgent = appName + "/" + appVersion + " " +
systemUserAgent.substring(systemUserAgent.indexOf("("), systemUserAgent.lastIndexOf(")") - 1) +
"; Flavour " + flavorDescription +
"; MatrixAndroidSDK_X " + BuildConfig.VERSION_NAME + ")"
"; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")"
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.room
import org.matrix.android.sdk.api.session.room.model.RoomStrippedState
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 GetRoomSummaryTask : Task<GetRoomSummaryTask.Params, RoomStrippedState> {
data class Params(
val roomId: String,
val viaServers: List<String>?
)
}
internal class DefaultGetRoomSummaryTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver
) : GetRoomSummaryTask {
override suspend fun execute(params: GetRoomSummaryTask.Params): RoomStrippedState {
return executeRequest(globalErrorReceiver) {
roomAPI.getRoomSummary(params.roomId, params.viaServers)
}
}
}

View File

@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomStrippedState
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.util.JsonDict
@ -254,7 +255,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}")
suspend fun join(@Path("roomIdOrAlias") roomIdOrAlias: String,
@Query("server_name") viaServers: List<String>,
@Body params: JsonDict): JoinRoomResponse
@Body params: JsonDict): JoinRoomResponse
/**
* Leave the given room.
@ -381,4 +382,14 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/upgrade")
suspend fun upgradeRoom(@Path("roomId") roomId: String,
@Body body: RoomUpgradeBody): RoomUpgradeResponse
/**
* The API returns the summary of the specified room, if the room could be found and the client should be able to view
* its contents according to the join_rules, history visibility, space membership and similar rules outlined in MSC3173
* as well as if the user is already a member of that room.
* https://github.com/deepbluev7/matrix-doc/blob/room-summaries/proposals/3266-room-summary.md
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "im.nheko.summary/rooms/{roomIdOrAlias}/summary")
suspend fun getRoomSummary(@Path("roomIdOrAlias") roomidOrAlias: String,
@Query("via") viaServers: List<String>?): RoomStrippedState
}

View File

@ -253,4 +253,7 @@ internal abstract class RoomModule {
@Binds
abstract fun bindSign3pidInvitationTask(task: DefaultSign3pidInvitationTask): Sign3pidInvitationTask
@Binds
abstract fun bindGetRoomSummaryTask(task: DefaultGetRoomSummaryTask): GetRoomSummaryTask
}

View File

@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFi
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.internal.session.room.GetRoomSummaryTask
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
@ -49,6 +50,7 @@ internal class DefaultPeekRoomTask @Inject constructor(
private val getRoomIdByAliasTask: GetRoomIdByAliasTask,
private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
private val getPublicRoomTask: GetPublicRoomTask,
private val getRoomSummaryTask: GetRoomSummaryTask,
private val resolveRoomStateTask: ResolveRoomStateTask
) : PeekRoomTask {
@ -70,6 +72,25 @@ internal class DefaultPeekRoomTask @Inject constructor(
serverList = emptyList()
}
// If the room summary API is available on the Home Server we should try it first
val strippedState = tryOrNull("Failed to get room stripped state roomId:$roomId") {
getRoomSummaryTask.execute(GetRoomSummaryTask.Params(roomId, serverList))
}
if (strippedState != null) {
return PeekResult.Success(
roomId = strippedState.roomId,
alias = strippedState.getPrimaryAlias() ?: params.roomIdOrAlias.takeIf { isAlias },
avatarUrl = strippedState.avatarUrl,
name = strippedState.name,
topic = strippedState.topic,
numJoinedMembers = strippedState.numJoinedMembers,
viaServers = serverList,
roomType = strippedState.roomType,
someMembers = null,
isPublic = strippedState.worldReadable
)
}
// Is it a public room?
val visibilityRes = tryOrNull("## PEEK: failed to get visibility") {
getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))

View File

@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
@ -31,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.VersioningState
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.crypto.EventDecryptor
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
@ -207,63 +209,102 @@ internal class RoomSummaryUpdater @Inject constructor(
}
.toMap()
lookupMap.keys.forEach { lookedUp ->
if (lookedUp.roomType == RoomType.SPACE) {
// get childrens
// First handle child relations
lookupMap.keys.asSequence()
.filter { it.roomType == RoomType.SPACE }
.forEach { lookedUp ->
// get childrens
lookedUp.children.clearWith { it.deleteFromRealm() }
lookedUp.children.clearWith { it.deleteFromRealm() }
RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child ->
RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child ->
lookedUp.children.add(
realm.createObject<SpaceChildSummaryEntity>().apply {
this.childRoomId = child.roomId
this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst()
this.order = child.order
lookedUp.children.add(
realm.createObject<SpaceChildSummaryEntity>().apply {
this.childRoomId = child.roomId
this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst()
this.order = child.order
// this.autoJoin = child.autoJoin
this.viaServers.addAll(child.viaServers)
}
)
this.viaServers.addAll(child.viaServers)
}
)
RoomSummaryEntity.where(realm, child.roomId)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
.findFirst()
?.let { childSum ->
lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry ->
if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) {
// add looked up as a parent
entry.value.add(childSum)
RoomSummaryEntity.where(realm, child.roomId)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
.findFirst()
?.let { childSum ->
lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry ->
if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) {
// add looked up as a parent
entry.value.add(childSum)
}
}
}
}
}
// Now let's check parent relations
lookupMap.keys
.forEach { lookedUp ->
lookedUp.parents.clearWith { it.deleteFromRealm() }
// can we check parent relations here??
/**
* rooms can claim parents via the m.space.parent state event.
* canonical determines whether this is the main parent for the space.
*
* To avoid abuse where a room admin falsely claims that a room is part of a space that it should not be,
* clients could ignore such m.space.parent events unless either
* (a) there is a corresponding m.space.child event in the claimed parent, or
* (b) the sender of the m.space.child event has a sufficient power-level to send such an m.space.child event in the parent.
* (It is not necessarily required that that user currently be a member of the parent room -
* only the m.room.power_levels event is inspected.)
* [Checking the power-level rather than requiring an actual m.space.child event in the parent allows for "secret" rooms (see below).]
*/
RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions()
.map { parentInfo ->
// Is it a valid parent relation?
// Check if it's a child of the parent?
val isValidRelation: Boolean
val parent = lookupMap.firstNotNullOfOrNull { if (it.key.roomId == parentInfo.roomId) it.value else null }
if (parent?.firstOrNull { it.roomId == lookedUp.roomId } != null) {
// there is a corresponding m.space.child event in the claimed parent
isValidRelation = true
} else {
// check if sender can post child relation in parent?
val senderId = parentInfo.stateEventSender
val parentRoomId = parentInfo.roomId
val powerLevelsHelper = CurrentStateEventEntity
.getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS)
?.root
?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() }
?.let { PowerLevelsHelper(it) }
isValidRelation = powerLevelsHelper?.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) ?: false
}
if (isValidRelation) {
lookedUp.parents.add(
realm.createObject<SpaceParentSummaryEntity>().apply {
this.parentRoomId = parentInfo.roomId
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
this.canonical = parentInfo.canonical
this.viaServers.addAll(parentInfo.viaServers)
}
)
RoomSummaryEntity.where(realm, parentInfo.roomId)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
.findFirst()
?.let { parentSum ->
if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) {
// add lookedup as a parent
lookupMap[parentSum]?.add(lookedUp)
}
}
}
}
}
} else {
lookedUp.parents.clearWith { it.deleteFromRealm() }
// can we check parent relations here??
RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions()
.map { parentInfo ->
lookedUp.parents.add(
realm.createObject<SpaceParentSummaryEntity>().apply {
this.parentRoomId = parentInfo.roomId
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
this.canonical = parentInfo.canonical
this.viaServers.addAll(parentInfo.viaServers)
}
)
RoomSummaryEntity.where(realm, parentInfo.roomId)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
.findFirst()
?.let { parentSum ->
if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) {
// add lookedup as a parent
lookupMap[parentSum]?.add(lookedUp)
}
}
}
}
}
// Simple algorithm to break cycles
// Need more work to decide how to break, probably need to be as consistent as possible

View File

@ -89,7 +89,6 @@ internal class DefaultSpace(
body = SpaceChildContent(
order = null,
via = null,
// autoJoin = null,
suggested = null
).toContent()
)

View File

@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.powerlevels.Role
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
import org.matrix.android.sdk.api.session.space.Space
@ -77,7 +78,7 @@ internal class DefaultSpaceService @Inject constructor(
if (isPublic) {
this.roomAliasName = roomAliasLocalPart
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
invite = 0
invite = if (isPublic) Role.Default.value else Role.Moderator.value
)
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
@ -221,4 +222,23 @@ internal class DefaultSpaceService @Inject constructor(
).toContent()
)
}
override suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String) {
val room = roomGetter.getRoom(childRoomId)
?: throw IllegalArgumentException("Unknown Room $childRoomId")
val existingEvent = room.getStateEvent(EventType.STATE_SPACE_PARENT, QueryStringValue.Equals(parentSpaceId))
if (existingEvent != null) {
// Should i check if it was sent by me?
// we don't check power level, it will throw if you cannot do that
room.sendStateEvent(
eventType = EventType.STATE_SPACE_PARENT,
stateKey = parentSpaceId,
body = SpaceParentContent(
via = null,
canonical = null
).toContent()
)
}
}
}

View File

@ -19,14 +19,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
android {
compileSdkVersion 30
compileSdk versions.compileSdk
defaultConfig {
minSdkVersion 19
targetSdkVersion 30
versionCode 1
versionName "1.0"
minSdk versions.minSdk
targetSdk versions.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
@ -41,11 +38,11 @@ android {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation "androidx.fragment:fragment-ktx:1.3.6"
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation libs.jetbrains.kotlinStdlibJdk7
implementation libs.androidx.appCompat
implementation libs.androidx.fragmentKtx
implementation libs.androidx.exifinterface
// Log
implementation 'com.jakewharton.timber:timber:5.0.1'
implementation libs.jakewharton.timber
}

View File

@ -162,7 +162,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===105
enum class===106
### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3

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