Merge branch 'develop' into feature/aris/threads

# Conflicts:
#	.github/workflows/integration.yml
#	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/TimelineFragment.kt
#	vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt
This commit is contained in:
ariskotsomitopoulos 2022-01-12 18:20:50 +02:00
commit 9d48ecea2f
150 changed files with 916 additions and 320 deletions

View file

@ -1,90 +0,0 @@
name: Integration Test
on:
pull_request: { }
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
compile-android-test:
name: Compile Android 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: Compile Android tests
run: ./gradlew clean assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
integration-tests:
name: Integration Tests (Synapse)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
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:
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 https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh -o start.sh
chmod 777 start.sh
./start.sh --no-rate-limit
- name: Run integration tests on API ${{ matrix.api-level }}
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
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
script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.threads.ThreadMessagingTest matrix-sdk-android:connectedAndroidTest --info

208
.github/workflows/integration_tests.yml vendored Normal file
View file

@ -0,0 +1,208 @@
name: Integration Tests
on:
pull_request: { }
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
CI_GRADLE_ARG_PROPERTIES: >
-Porg.gradle.jvmargs=-Xmx2g
-Porg.gradle.parallel=false
jobs:
# Build Android Tests [Matrix SDK]
build-android-test-matrix-sdk:
name: Matrix SDK - Build Android 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: Build Android Tests for matrix-sdk-android
run: ./gradlew clean matrix-sdk-android:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
# Build Android Tests [Matrix APP]
build-android-test-app:
name: App - Build Android 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: Build Android Tests for vector
run: ./gradlew clean vector:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
# Run Android Tests
integration-tests:
name: Matrix SDK - Running Integration Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
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:
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 https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh -o start.sh
chmod 777 start.sh
./start.sh --no-rate-limit
# package: org.matrix.android.sdk.session
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.session] API[${{ matrix.api-level }}]
continue-on-error: true
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest
- name: Read Results [org.matrix.android.sdk.session]
continue-on-error: true
id: get-comment-body-session
run: |
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
echo "::set-output name=session::passed=$body"
# package: org.matrix.android.sdk.account
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.account] API[${{ matrix.api-level }}]
continue-on-error: true
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest
- name: Read Results [org.matrix.android.sdk.account]
continue-on-error: true
id: get-comment-body-account
run: |
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
echo "::set-output name=account::passed=$body"
# package: org.matrix.android.sdk.internal
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.internal] API[${{ matrix.api-level }}]
continue-on-error: true
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest
- name: Read Results [org.matrix.android.sdk.internal]
continue-on-error: true
id: get-comment-body-internal
run: |
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
echo "::set-output name=internal::passed=$body"
# package: org.matrix.android.sdk.ordering
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.ordering] API[${{ matrix.api-level }}]
continue-on-error: true
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest
- name: Read Results [org.matrix.android.sdk.ordering]
continue-on-error: true
id: get-comment-body-ordering
run: |
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
echo "::set-output name=ordering::passed=$body"
# package: class PermalinkParserTest
- name: Run integration tests for Matrix SDK class [org.matrix.android.sdk.PermalinkParserTest] API[${{ matrix.api-level }}]
continue-on-error: true
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
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 -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest
- name: Read Results [org.matrix.android.sd.PermalinkParserTest]
continue-on-error: true
id: get-comment-body-permalink
run: |
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
echo "::set-output name=permalink::passed=$body"
- name: Find Comment
uses: peter-evans/find-comment@v1
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Integration Tests Results
- name: Publish results to PR
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
### Matrix SDK
## Integration Tests Results:
- `[org.matrix.android.sdk.session]`<br>${{ steps.get-comment-body-session.outputs.session }}
- `[org.matrix.android.sdk.account]`<br>${{ steps.get-comment-body-account.outputs.account }}
- `[org.matrix.android.sdk.internal]`<br>${{ steps.get-comment-body-internal.outputs.internal }}
- `[org.matrix.android.sdk.ordering]`<br>${{ steps.get-comment-body-ordering.outputs.ordering }}
- `[org.matrix.android.sdk.PermalinkParserTest]`<br>${{ steps.get-comment-body-permalink.outputs.permalink }}
edit-mode: replace
## Useful commands
# script: ./integration_tests_script.sh
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest --info
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES matrix-sdk-android:connectedAndroidTest --info
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest --info

View file

@ -1,3 +1,56 @@
Changes in Element v1.3.14 (2022-01-12)
=======================================
Bugfixes 🐛
----------
- Fix sending events in encrypted rooms broken, and incremental sync broken in 1.3.13 ([#4924](https://github.com/vector-im/element-android/issues/4924))
Changes in Element v1.3.13 (2022-01-11)
=======================================
Features ✨
----------
- Updates onboarding splash screen to have a dedicated sign in button and removes the dual purpose sign in/up stage ([#4382](https://github.com/vector-im/element-android/issues/4382))
- Display Analytics opt-in screen at first start-up of the app ([#4892](https://github.com/vector-im/element-android/issues/4892))
- New attachment picker UI ([#3444](https://github.com/vector-im/element-android/issues/3444))
- Add labs support for rendering LaTeX maths (MSC2191) ([#2133](https://github.com/vector-im/element-android/issues/2133))
- Allow changing nick colors from the member detail screen ([#2614](https://github.com/vector-im/element-android/issues/2614))
- Analytics: Track Errors ([#4719](https://github.com/vector-im/element-android/issues/4719))
- Change internal timeline management. ([#4405](https://github.com/vector-im/element-android/issues/4405))
- Translate the error observed when the user is not allowed to join a room ([#4847](https://github.com/vector-im/element-android/issues/4847))
Bugfixes 🐛
----------
- Stop using CharSequence as EpoxyAttribute because it can lead to crash if the CharSequence mutates during rendering. ([#4837](https://github.com/vector-im/element-android/issues/4837))
- Better handling of misconfigured room encryption ([#4711](https://github.com/vector-im/element-android/issues/4711))
- Fix message replies/quotes to respect newlines. ([#4540](https://github.com/vector-im/element-android/issues/4540))
- Polls: unable to create a poll with more than 10 answers ([#4735](https://github.com/vector-im/element-android/issues/4735))
- Fix for broken unread message indicator on the room list when there are no messages in the room. ([#4749](https://github.com/vector-im/element-android/issues/4749))
- Fixes newer emojis rendering strangely when inserting from the system keyboard ([#4756](https://github.com/vector-im/element-android/issues/4756))
- Fixing unable to change change avatar in some scenarios ([#4767](https://github.com/vector-im/element-android/issues/4767))
- Tentative fix for the speaker being used instead of earpiece for the outgoing call ringtone on lineage os ([#4781](https://github.com/vector-im/element-android/issues/4781))
- Fixing crashes when quickly scrolling or restoring the room timeline ([#4789](https://github.com/vector-im/element-android/issues/4789))
- Fixing encrypted non message events showing up as notification messages (eg when a participant joins, mutes or leaves a voice call) ([#4804](https://github.com/vector-im/element-android/issues/4804))
SDK API changes ⚠️
------------------
- Introduce method onStateUpdated on Timeline.Callback ([#4405](https://github.com/vector-im/element-android/issues/4405))
- Support tagged events in Room Account Data (MSC2437) ([#4753](https://github.com/vector-im/element-android/issues/4753))
Other changes
-------------
- Workaround to fetch all the pending toDevice events from a Synapse homeserver ([#4612](https://github.com/vector-im/element-android/issues/4612))
- Toolbar is added to a views with QR code scan ([#4644](https://github.com/vector-im/element-android/issues/4644))
- Open share UI provides by the system when sharing media or text. ([#4745](https://github.com/vector-im/element-android/issues/4745))
- Cleaning rendering of state events in timeline ([#4747](https://github.com/vector-im/element-android/issues/4747))
- Enabling new FTUE Auth onboarding base, includes the "I already have an account" button in the splash ([#4872](https://github.com/vector-im/element-android/issues/4872))
- Olm lib is now hosted in MavenCentral - upgrade to 3.2.10 ([#4882](https://github.com/vector-im/element-android/issues/4882))
- Remove deprecated experimental restricted space lab option ([#4889](https://github.com/vector-im/element-android/issues/4889))
- Add ktlint results on github as a comment only on fail ([#4888](https://github.com/vector-im/element-android/issues/4888))
- Fix github actions ktlint reports and publish results on PR as comment ([#4864](https://github.com/vector-im/element-android/issues/4864))
Changes in Element v1.3.12 (2021-12-20)
=======================================

View file

@ -139,7 +139,7 @@ If a string is not used anymore, it should be removed from the resource, but ple
Instead, please comment the original string with:
```xml
<!-- TO BE REMOVED -->
<!-- TODO TO BE REMOVED -->
```
The string will be removed during the next sync with Weblate.

View file

@ -1 +0,0 @@
Add labs support for rendering LaTeX maths (MSC2191)

View file

@ -1 +0,0 @@
Allow changing nick colors from the member detail screen

View file

@ -1 +0,0 @@
New attachment picker UI

View file

@ -1 +0,0 @@
Updates onboarding splash screen to have a dedicated sign in button and removes the dual purpose sign in/up stage

View file

@ -1 +0,0 @@
Change internal timeline management.

View file

@ -1 +0,0 @@
Introduce method onStateUpdated on Timeline.Callback

View file

@ -1 +0,0 @@
Fix message replies/quotes to respect newlines.

View file

@ -1 +0,0 @@
Workaround to fetch all the pending toDevice events from a Synapse homeserver

View file

@ -1 +0,0 @@
Toolbar is added to a views with QR code scan

View file

@ -1 +0,0 @@
Analytics: Track Errors

View file

@ -1 +0,0 @@
Polls: unable to create a poll with more than 10 answers

View file

@ -1 +0,0 @@
Open share UI provides by the system when sharing media or text.

View file

@ -1 +0,0 @@
Cleaning rendering of state events in timeline

View file

@ -1 +0,0 @@
Fix for broken unread message indicator on the room list when there are no messages in the room.

View file

@ -1 +0,0 @@
Support tagged events in Room Account Data (MSC2437)

View file

@ -1 +0,0 @@
Fixes newer emojis rendering strangely when inserting from the system keyboard

View file

@ -1 +0,0 @@
Fixing unable to change change avatar in some scenarios

View file

@ -1 +0,0 @@
Tentative fix for the speaker being used instead of earpiece for the outgoing call ringtone on lineage os

View file

@ -1 +0,0 @@
Fixing crashes when quickly scrolling or restoring the room timeline

View file

@ -1 +0,0 @@
Fixing encrypted non message events showing up as notification messages (eg when a participant joins, mutes or leaves a voice call)

View file

@ -1 +0,0 @@
Stop using CharSequence as EpoxyAttribute because it can lead to crash if the CharSequence mutates during rendering.

1
changelog.d/4842.misc Normal file
View file

@ -0,0 +1 @@
Fix integration tests and add a comment with results (still not perfect due to github actions resource limitations)

View file

@ -1 +0,0 @@
Translate the error observed when the user is not allowed to join a room

View file

@ -1 +0,0 @@
Fix github actions ktlint reports and publish results on PR as comment

View file

@ -1 +0,0 @@
Enabling new FTUE Auth onboarding base, includes the "I already have an account" button in the splash

View file

@ -1 +0,0 @@
Olm lib is now hosted in MavenCentral - upgrade to 3.2.10

View file

@ -1 +0,0 @@
Add ktlint results on github as a comment only on fail

View file

@ -1 +0,0 @@
Remove deprecated experimental restricted space lab option

View file

@ -1 +0,0 @@
Display Analytics opt-in screen at first start-up of the app

1
changelog.d/4926.misc Normal file
View file

@ -0,0 +1 @@
Add signing config for the release buildType. No secret added

View file

@ -29,6 +29,7 @@ def vanniktechEmoji = "0.8.0"
def mockk = "1.12.1"
def espresso = "3.4.0"
def androidxTest = "1.4.0"
def androidxOrchestrator = "1.4.1"
ext.libs = [
@ -63,7 +64,7 @@ ext.libs = [
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
'coreTesting' : "androidx.arch.core:core-testing:2.1.0",
'testCore' : "androidx.test:core:$androidxTest",
'orchestrator' : "androidx.test:orchestrator:$androidxTest",
'orchestrator' : "androidx.test:orchestrator:$androidxOrchestrator",
'testRunner' : "androidx.test:runner:$androidxTest",
'testRules' : "androidx.test:rules:$androidxTest",
'espressoCore' : "androidx.test.espresso:espresso-core:$espresso",

View file

@ -0,0 +1,2 @@
Main changes in this version: First change in onboarding screens, including Analytics opt-in. Support for Events with Math added in the labs.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View file

@ -0,0 +1,2 @@
Main changes in this version: First change in onboarding screens, including Analytics opt-in. Support for Events with Math added in the labs.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View file

@ -25,4 +25,10 @@ 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
#vector.httpLogLevel=BODY
# Dummy values for signing secrets
signing.element.storePath=pathTo.keystore
signing.element.storePassword=Secret
signing.element.keyId=Secret
signing.element.keyPassword=Secret

3
integration_tests_script.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
./gradlew -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest
./gradlew -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.TimelineForwardPaginationTest matrix-sdk-android:connectedAndroidTest

View file

@ -0,0 +1,3 @@
#!/bin/bash
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.TimelineForwardPaginationTest matrix-sdk-android:connectedAndroidTest

View file

@ -31,7 +31,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.3.13\""
buildConfigField "String", "SDK_VERSION", "\"1.3.16\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
@ -66,6 +66,7 @@ android {
adbOptions {
installOptions "-g"
// timeOutInMs 350 * 1000
}
compileOptions {
@ -160,7 +161,7 @@ dependencies {
implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.40'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41'
testImplementation libs.tests.junit
testImplementation 'org.robolectric:robolectric:4.7.3'

View file

@ -16,7 +16,9 @@
package org.matrix.android.sdk.account
import androidx.test.filters.LargeTest
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@ -29,6 +31,7 @@ import org.matrix.android.sdk.common.TestConstants
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class AccountCreationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@ -42,6 +45,7 @@ class AccountCreationTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun createAccountAndLoginAgainTest() {
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.account
import org.amshove.kluent.shouldBeTrue
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@ -30,6 +31,7 @@ import org.matrix.android.sdk.common.TestConstants
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@Ignore("This test will be ignored until it is fixed")
class ChangePasswordTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())

View file

@ -0,0 +1,52 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.common
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
/**
* Retry test rule used to retry test that failed.
* Retry failed test 3 times
*/
class RetryTestRule(val retryCount: Int = 3) : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return statement(base)
}
private fun statement(base: Statement): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
var caughtThrowable: Throwable? = null
// implement retry logic here
for (i in 0 until retryCount) {
try {
base.evaluate()
return
} catch (t: Throwable) {
caughtThrowable = t
}
}
throw caughtThrowable!!
}
}
}
}

View file

@ -22,8 +22,8 @@ object TestConstants {
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
// Time out to use when waiting for server response. 20s
private const val AWAIT_TIME_OUT_MILLIS = 20_000
// Time out to use when waiting for server response.
private const val AWAIT_TIME_OUT_MILLIS = 30_000
// Time out to use when waiting for server response, when the debugger is connected. 10 minutes
private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000

View file

@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -40,6 +41,7 @@ class PreShareKeysTest : InstrumentedTest {
private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
@Ignore("This test will be ignored until it is fixed")
fun ensure_outbound_session_happy_path() {
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
val e2eRoomID = testData.roomId
@ -97,7 +99,6 @@ class PreShareKeysTest : InstrumentedTest {
}
}
testHelper.signOutAndClose(aliceSession)
testHelper.signOutAndClose(bobSession)
testData.cleanUp(testHelper)
}
}

View file

@ -21,6 +21,7 @@ import org.amshove.kluent.shouldBe
import org.junit.Assert
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -84,6 +85,7 @@ class UnwedgingTest : InstrumentedTest {
* -> This is automatically fixed after SDKs restarted the olm session
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testUnwedging() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.crypto.crosssigning
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@ -24,6 +25,7 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -43,6 +45,7 @@ import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@LargeTest
class XSigningTest : InstrumentedTest {
private val testHelper = CommonTestHelper(context())
@ -124,11 +127,11 @@ class XSigningTest : InstrumentedTest {
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
testHelper.signOutAndClose(aliceSession)
testHelper.signOutAndClose(bobSession)
cryptoTestData.cleanUp(testHelper)
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_CrossSigningTestAliceTrustBobNewDevice() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()

View file

@ -18,12 +18,14 @@ package org.matrix.android.sdk.internal.crypto.gossiping
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertTrue
import junit.framework.TestCase.fail
import org.junit.Assert
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -59,11 +61,13 @@ import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class KeyShareTests : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_DoNotSelfShareIfNotTrusted() {
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
@ -195,6 +199,7 @@ class KeyShareTests : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_ShareSSSSSecret() {
val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
@ -307,6 +312,7 @@ class KeyShareTests : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_ImproperKeyShareBug() {
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))

View file

@ -18,8 +18,10 @@ package org.matrix.android.sdk.internal.crypto.gossiping
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.Assert
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -39,12 +41,14 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class WithHeldTests : InstrumentedTest {
private val testHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_WithHeldUnverifiedReason() {
// =============================
// ARRANGE
@ -129,6 +133,7 @@ class WithHeldTests : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_WithHeldNoOlm() {
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession
@ -199,6 +204,7 @@ class WithHeldTests : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_WithHeldKeyRequest() {
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession

View file

@ -17,12 +17,14 @@
package org.matrix.android.sdk.internal.crypto.keysbackup
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -47,6 +49,7 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class KeysBackupTest : InstrumentedTest {
private val testHelper = CommonTestHelper(context())
@ -59,6 +62,7 @@ class KeysBackupTest : InstrumentedTest {
* - Reset keys backup markers
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun roomKeysTest_testBackupStore_ok() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
@ -157,6 +161,7 @@ class KeysBackupTest : InstrumentedTest {
* - Check the backup completes
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun backupAfterCreateKeysBackupVersionTest() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
@ -197,6 +202,7 @@ class KeysBackupTest : InstrumentedTest {
* Check that backupAllGroupSessions() returns valid data
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun backupAllGroupSessionsTest() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
@ -241,6 +247,7 @@ class KeysBackupTest : InstrumentedTest {
* - Compare the decrypted megolm key with the original one
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testEncryptAndDecryptKeysBackupData() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
@ -282,6 +289,7 @@ class KeysBackupTest : InstrumentedTest {
* - Restore must be successful
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun restoreKeysBackupTest() {
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
@ -365,6 +373,7 @@ class KeysBackupTest : InstrumentedTest {
* - It must be trusted and must have with 2 signatures now
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun trustKeyBackupVersionTest() {
// - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device
@ -424,6 +433,7 @@ class KeysBackupTest : InstrumentedTest {
* - It must be trusted and must have with 2 signatures now
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun trustKeyBackupVersionWithRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device
@ -481,6 +491,7 @@ class KeysBackupTest : InstrumentedTest {
* - The backup must still be untrusted and disabled
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device
@ -522,6 +533,7 @@ class KeysBackupTest : InstrumentedTest {
* - It must be trusted and must have with 2 signatures now
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun trustKeyBackupVersionWithPasswordTest() {
val password = "Password"
@ -581,6 +593,7 @@ class KeysBackupTest : InstrumentedTest {
* - The backup must still be untrusted and disabled
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun trustKeyBackupVersionWithWrongPasswordTest() {
val password = "Password"
val badPassword = "Bad Password"
@ -621,6 +634,7 @@ class KeysBackupTest : InstrumentedTest {
* - It must fail
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
@ -654,6 +668,7 @@ class KeysBackupTest : InstrumentedTest {
* - Restore must be successful
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testBackupWithPassword() {
val password = "password"
@ -709,6 +724,7 @@ class KeysBackupTest : InstrumentedTest {
* - It must fail
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun restoreKeysBackupWithAWrongPasswordTest() {
val password = "password"
val wrongPassword = "passw0rd"
@ -745,6 +761,7 @@ class KeysBackupTest : InstrumentedTest {
* - Restore must be successful
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
val password = "password"
@ -773,6 +790,7 @@ class KeysBackupTest : InstrumentedTest {
* - It must fail
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
@ -804,6 +822,7 @@ class KeysBackupTest : InstrumentedTest {
* - Check the returned KeysVersionResult is trusted
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testIsKeysBackupTrusted() {
// - Create a backup version
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
@ -847,6 +866,7 @@ class KeysBackupTest : InstrumentedTest {
* -> The new alice session must back up to the same version
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() {
// - Create a backup version
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
@ -978,6 +998,7 @@ class KeysBackupTest : InstrumentedTest {
* -> It must success
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun testBackupAfterVerifyingADevice() {
// - Create a backup version
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()

View file

@ -22,6 +22,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -47,8 +48,6 @@ import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorage
@FixMethodOrder(MethodSorters.JVM)
class QuadSTests : InstrumentedTest {
private val testHelper = CommonTestHelper(context())
private val emptyKeySigner = object : KeySigner {
override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
return null
@ -57,6 +56,8 @@ class QuadSTests : InstrumentedTest {
@Test
fun test_Generate4SKey() {
val testHelper = CommonTestHelper(context())
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService
@ -108,6 +109,8 @@ class QuadSTests : InstrumentedTest {
@Test
fun test_StoreSecret() {
val testHelper = CommonTestHelper(context())
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId = "My.Key"
val info = generatedSecret(aliceSession, keyId, true)
@ -151,6 +154,8 @@ class QuadSTests : InstrumentedTest {
@Test
fun test_SetDefaultLocalEcho() {
val testHelper = CommonTestHelper(context())
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService
@ -171,6 +176,8 @@ class QuadSTests : InstrumentedTest {
@Test
fun test_StoreSecretWithMultipleKey() {
val testHelper = CommonTestHelper(context())
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId1 = "Key.1"
val key1Info = generatedSecret(aliceSession, keyId1, true)
@ -217,7 +224,10 @@ class QuadSTests : InstrumentedTest {
}
@Test
@Ignore("Test is working locally, not in GitHub actions")
fun test_GetSecretWithBadPassphrase() {
val testHelper = CommonTestHelper(context())
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId1 = "Key.1"
val passphrase = "The good pass phrase"
@ -264,6 +274,8 @@ class QuadSTests : InstrumentedTest {
}
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
val testHelper = CommonTestHelper(context())
var accountData: UserAccountDataEvent? = null
testHelper.waitWithLatch {
val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
@ -281,6 +293,7 @@ class QuadSTests : InstrumentedTest {
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService
val testHelper = CommonTestHelper(context())
val creationInfo = testHelper.runBlockingTest {
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
@ -300,6 +313,7 @@ class QuadSTests : InstrumentedTest {
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService
val testHelper = CommonTestHelper(context())
val creationInfo = testHelper.runBlockingTest {
quadS.generateKeyWithPassphrase(

View file

@ -25,6 +25,7 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -53,11 +54,11 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SASTest : InstrumentedTest {
private val testHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
fun test_aliceStartThenAliceCancel() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
@ -137,7 +138,10 @@ class SASTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_key_agreement_protocols_must_include_curve25519() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
fail("Not passing for the moment")
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
@ -194,7 +198,10 @@ class SASTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_key_agreement_macs_Must_include_hmac_sha256() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
fail("Not passing for the moment")
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
@ -232,7 +239,10 @@ class SASTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_key_agreement_short_code_include_decimal() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
fail("Not passing for the moment")
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
@ -303,6 +313,8 @@ class SASTest : InstrumentedTest {
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
@Test
fun test_aliceStartTwoRequests() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
@ -342,7 +354,10 @@ class SASTest : InstrumentedTest {
* Test that when alice starts a 'correct' request, bob agrees.
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun test_aliceAndBobAgreement() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
@ -402,6 +417,8 @@ class SASTest : InstrumentedTest {
@Test
fun test_aliceAndBobSASCode() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
@ -458,6 +475,8 @@ class SASTest : InstrumentedTest {
@Test
fun test_happyPath() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
@ -527,9 +546,6 @@ class SASTest : InstrumentedTest {
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId)
val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().deviceId)
// latch wait a bit again
Thread.sleep(1000)
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
cryptoTestData.cleanUp(testHelper)
@ -537,6 +553,8 @@ class SASTest : InstrumentedTest {
@Test
fun test_ConcurrentStart() {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession

View file

@ -40,8 +40,6 @@ import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class VerificationTest : InstrumentedTest {
private val testHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(testHelper)
data class ExpectedResult(
val sasIsSupported: Boolean = false,
@ -155,6 +153,8 @@ class VerificationTest : InstrumentedTest {
bobSupportedMethods: List<VerificationMethod>,
expectedResultForAlice: ExpectedResult,
expectedResultForBob: ExpectedResult) {
val testHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(testHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession

View file

@ -21,6 +21,7 @@ import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
import org.junit.Assert.assertEquals
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@ -132,6 +133,7 @@ class MarkdownParserTest : InstrumentedTest {
* Note: the test is not passing, it does not work on Element Web neither
*/
@Test
@Ignore("This test will be ignored until it is fixed")
fun parseStrike_not_passing() {
testType(
name = "strike",
@ -141,6 +143,7 @@ class MarkdownParserTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun parseStrikeNewLines() {
testTypeNewLines(
name = "strike",
@ -160,6 +163,7 @@ class MarkdownParserTest : InstrumentedTest {
// TODO. Improve testTypeNewLines function to cover <pre><code class="language-code">test</code></pre>
@Test
@Ignore("This test will be ignored until it is fixed")
fun parseCodeNewLines_not_passing() {
testTypeNewLines(
name = "code",
@ -179,6 +183,7 @@ class MarkdownParserTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun parseCode2NewLines_not_passing() {
testTypeNewLines(
name = "code",
@ -197,6 +202,7 @@ class MarkdownParserTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun parseCode3NewLines_not_passing() {
testTypeNewLines(
name = "code",
@ -233,6 +239,7 @@ class MarkdownParserTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun parseQuote_not_passing() {
"> quoted\nline2".let { markdownParser.parse(it).expect(it, "<blockquote><p>quoted<br />line2</p></blockquote>") }
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.matrix.android.sdk
package org.matrix.android.sdk.ordering
import org.amshove.kluent.internal.assertEquals
import org.junit.Assert

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.matrix.android.sdk
package org.matrix.android.sdk.ordering
import org.amshove.kluent.internal.assertEquals
import org.junit.Assert

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.session.room.timeline
import androidx.test.filters.LargeTest
import kotlinx.coroutines.runBlocking
import org.amshove.kluent.internal.assertEquals
import org.amshove.kluent.shouldBeFalse
@ -40,16 +41,20 @@ import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class TimelineForwardPaginationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
// @Rule
// @JvmField
// val mRetryTestRule = RetryTestRule()
/**
* This test ensure that if we click to permalink, we will be able to go back to the live
*/
@Test
fun forwardPaginationTest() {
val commonTestHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
val numberOfMessagesToSend = 90
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.session.room.timeline
import androidx.test.filters.LargeTest
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue
import org.junit.FixMethodOrder
@ -38,16 +39,17 @@ import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class TimelinePreviousLastForwardTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
/**
* This test ensure that if we have a chunk in the timeline which is due to a sync, and we click to permalink, we will be able to go back to the live
*/
@Test
fun previousLastForwardTest() {
val commonTestHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
val aliceSession = cryptoTestData.firstSession

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.session.room.timeline
import androidx.test.filters.LargeTest
import kotlinx.coroutines.runBlocking
import org.amshove.kluent.internal.assertEquals
import org.junit.FixMethodOrder
@ -36,13 +37,13 @@ import org.matrix.android.sdk.common.TestConstants
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class TimelineSimpleBackPaginationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
@Test
fun timeline_backPaginate_shouldReachEndOfTimeline() {
val commonTestHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
val numberOfMessagesToSent = 200
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)

View file

@ -16,8 +16,10 @@
package org.matrix.android.sdk.session.room.timeline
import androidx.test.filters.LargeTest
import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@ -31,8 +33,13 @@ import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
import java.util.concurrent.CountDownLatch
/** !! Not working with the new timeline
* Disabling it until the fix is made
*/
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@Ignore("This test will be ignored until it is fixed")
@LargeTest
class TimelineWithManyMembersTest : InstrumentedTest {
companion object {
@ -45,6 +52,7 @@ class TimelineWithManyMembersTest : InstrumentedTest {
/**
* Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
*/
@Test
fun everyone_should_decrypt_message_in_a_crowded_room() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)

View file

@ -37,9 +37,6 @@ class SearchMessagesTest : InstrumentedTest {
private const val MESSAGE = "Lorem ipsum dolor sit amet"
}
private val commonTestHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
@Test
fun sendTextMessageAndSearchPartOfItUsingSession() {
doTest { cryptoTestData ->
@ -76,6 +73,8 @@ class SearchMessagesTest : InstrumentedTest {
}
private fun doTest(block: suspend (CryptoTestData) -> SearchResult) {
val commonTestHelper = CommonTestHelper(context())
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.session.space
import androidx.test.filters.LargeTest
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@ -43,12 +44,12 @@ import org.matrix.android.sdk.common.SessionTestParams
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class SpaceCreationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@Test
fun createSimplePublicSpace() {
val commonTestHelper = CommonTestHelper(context())
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
val roomName = "My Space"
val topic = "A public space for test"
@ -58,6 +59,7 @@ class SpaceCreationTest : InstrumentedTest {
// wait a bit to let the summary update it self :/
it.countDown()
}
Thread.sleep(4_000)
val syncedSpace = session.spaceService().getSpace(spaceId)
commonTestHelper.waitWithLatch {
@ -99,6 +101,8 @@ class SpaceCreationTest : InstrumentedTest {
@Test
fun testJoinSimplePublicSpace() {
val commonTestHelper = CommonTestHelper(context())
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
@ -130,6 +134,7 @@ class SpaceCreationTest : InstrumentedTest {
@Test
fun testSimplePublicSpaceWithChildren() {
val commonTestHelper = CommonTestHelper(context())
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))

View file

@ -23,6 +23,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@ -50,10 +51,10 @@ import org.matrix.android.sdk.common.SessionTestParams
@FixMethodOrder(MethodSorters.JVM)
class SpaceHierarchyTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@Test
fun createCanonicalChildRelation() {
val commonTestHelper = CommonTestHelper(context())
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
val spaceName = "My Space"
val topic = "A public space for test"
@ -170,6 +171,7 @@ class SpaceHierarchyTest : InstrumentedTest {
@Test
fun testFilteringBySpace() {
val commonTestHelper = CommonTestHelper(context())
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
@ -236,7 +238,7 @@ class SpaceHierarchyTest : InstrumentedTest {
it.countDown()
}
Thread.sleep(2_000)
Thread.sleep(6_000)
val orphansUpdate = session.getRoomSummaries(roomSummaryQueryParams {
activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null)
})
@ -244,7 +246,9 @@ class SpaceHierarchyTest : InstrumentedTest {
}
@Test
@Ignore("This test will be ignored until it is fixed")
fun testBreakCycle() {
val commonTestHelper = CommonTestHelper(context())
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
@ -273,8 +277,6 @@ class SpaceHierarchyTest : InstrumentedTest {
it.countDown()
}
Thread.sleep(1000)
// A -> C -> A
val aChildren = session.getFlattenRoomSummaryChildrenOf(spaceAInfo.spaceId)
@ -288,6 +290,7 @@ class SpaceHierarchyTest : InstrumentedTest {
@Test
fun testLiveFlatChildren() {
val commonTestHelper = CommonTestHelper(context())
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
@ -374,6 +377,7 @@ class SpaceHierarchyTest : InstrumentedTest {
childInfo: List<Triple<String, Boolean, Boolean?>>
/** Name, auto-join, canonical*/
): TestSpaceCreationResult {
val commonTestHelper = CommonTestHelper(context())
var spaceId = ""
var roomIds: List<String> = emptyList()
commonTestHelper.waitWithLatch { latch ->
@ -401,6 +405,7 @@ class SpaceHierarchyTest : InstrumentedTest {
childInfo: List<Triple<String, Boolean, Boolean?>>
/** Name, auto-join, canonical*/
): TestSpaceCreationResult {
val commonTestHelper = CommonTestHelper(context())
var spaceId = ""
var roomIds: List<String> = emptyList()
commonTestHelper.waitWithLatch { latch ->
@ -435,6 +440,7 @@ class SpaceHierarchyTest : InstrumentedTest {
@Test
fun testRootSpaces() {
val commonTestHelper = CommonTestHelper(context())
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
/* val spaceAInfo = */ createPublicSpace(session, "SpaceA", listOf(
@ -459,9 +465,10 @@ class SpaceHierarchyTest : InstrumentedTest {
runBlocking {
val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
Thread.sleep(6_000)
}
Thread.sleep(2000)
// Thread.sleep(4_000)
// + A
// a1, a2
// + B
@ -478,6 +485,7 @@ class SpaceHierarchyTest : InstrumentedTest {
@Test
fun testParentRelation() {
val commonTestHelper = CommonTestHelper(context())
val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true))

View file

@ -27,5 +27,8 @@ enum class RoomEncryptionTrustLevel {
Warning,
// All devices in the room are verified -> the app should display a green shield
Trusted
Trusted,
// e2e is active but with an unsupported algorithm
E2EWithUnsupportedAlgorithm
}

View file

@ -27,9 +27,12 @@ interface RoomCryptoService {
fun shouldEncryptForInvitedMembers(): Boolean
/**
* Enable encryption of the room
* Enable encryption of the room.
* @param Use force to ensure that this algorithm will be used. Otherwise this call
* will throw if encryption is already setup or if the algorithm is not supported. Only to
* be used by admins to fix misconfigured encryption.
*/
suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM)
suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM, force: Boolean = false)
/**
* Ensures all members of the room are loaded and outbound session keys are shared.

View file

@ -0,0 +1,28 @@
/*
* 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 org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
sealed class RoomEncryptionAlgorithm {
abstract class SupportedAlgorithm(val alg: String) : RoomEncryptionAlgorithm()
object Megolm : SupportedAlgorithm(MXCRYPTO_ALGORITHM_MEGOLM)
data class UnsupportedAlgorithm(val name: String?) : RoomEncryptionAlgorithm()
}

View file

@ -62,7 +62,8 @@ data class RoomSummary(
val roomType: String? = null,
val spaceParents: List<SpaceParentInfo>? = null,
val spaceChildren: List<SpaceChildInfo>? = null,
val flattenParentIds: List<String> = emptyList()
val flattenParentIds: List<String> = emptyList(),
val roomEncryptionAlgorithm: RoomEncryptionAlgorithm? = null
) {
val isVersioned: Boolean

View file

@ -37,7 +37,6 @@ internal class CryptoSessionInfoProvider @Inject constructor(
fun isRoomEncrypted(roomId: String): Boolean {
val encryptionEvent = monarchy.fetchCopied { realm ->
EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
.isEmpty(EventEntityFields.STATE_KEY)
.findFirst()
}

View file

@ -177,7 +177,7 @@ internal class DefaultCryptoService @Inject constructor(
private val isStarted = AtomicBoolean(false)
fun onStateEvent(roomId: String, event: Event) {
when (event.getClearType()) {
when (event.type) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
@ -185,10 +185,13 @@ internal class DefaultCryptoService @Inject constructor(
}
fun onLiveEvent(roomId: String, event: Event) {
when (event.getClearType()) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
// handle state events
if (event.isStateEvent()) {
when (event.type) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
}
}
}
@ -575,26 +578,31 @@ internal class DefaultCryptoService @Inject constructor(
// (for now at least. Maybe we should alert the user somehow?)
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
if (!existingAlgorithm.isNullOrEmpty() && existingAlgorithm != algorithm) {
Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
if (existingAlgorithm == algorithm && roomEncryptorsStore.get(roomId) != null) {
// ignore
Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Ignoring m.room.encryption for same alg ($algorithm) in $roomId")
return false
}
val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm)
// Always store even if not supported
cryptoStore.storeRoomAlgorithm(roomId, algorithm)
if (!encryptingClass) {
Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm")
return false
}
cryptoStore.storeRoomAlgorithm(roomId, algorithm!!)
val alg: IMXEncrypting = when (algorithm) {
val alg: IMXEncrypting? = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> megolmEncryptionFactory.create(roomId)
else -> olmEncryptionFactory.create(roomId)
MXCRYPTO_ALGORITHM_OLM -> olmEncryptionFactory.create(roomId)
else -> null
}
roomEncryptorsStore.put(roomId, alg)
if (alg != null) {
roomEncryptorsStore.put(roomId, alg)
}
// if encryption was not previously enabled in this room, we will have been
// ignoring new device events for these users so far. We may well have
@ -927,6 +935,7 @@ internal class DefaultCryptoService @Inject constructor(
}
private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) {
if (!event.isStateEvent()) return
val eventContent = event.content.toModel<RoomHistoryVisibilityContent>()
eventContent?.historyVisibility?.let {
cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED)

View file

@ -27,7 +27,7 @@ data class EncryptionEventContent(
* Required. The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'.
*/
@Json(name = "algorithm")
val algorithm: String,
val algorithm: String?,
/**
* How long the session should be used before changing it. 604800000 (a week) is the recommended default.

View file

@ -230,7 +230,7 @@ internal interface IMXCryptoStore {
* @param roomId the id of the room.
* @param algorithm the algorithm.
*/
fun storeRoomAlgorithm(roomId: String, algorithm: String)
fun storeRoomAlgorithm(roomId: String, algorithm: String?)
/**
* Provides the algorithm used in a dedicated room.

View file

@ -629,7 +629,7 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun storeRoomAlgorithm(roomId: String, algorithm: String) {
override fun storeRoomAlgorithm(roomId: String, algorithm: String?) {
doRealmTransaction(realmConfiguration) {
CryptoRoomEntity.getOrCreate(it, roomId).algorithm = algorithm
}

View file

@ -26,6 +26,7 @@ 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.model.tag.RoomTag
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
import org.matrix.android.sdk.internal.database.model.ChunkEntityFields
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
@ -56,7 +57,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
) : RealmMigration {
companion object {
const val SESSION_STORE_SCHEMA_VERSION = 21L
const val SESSION_STORE_SCHEMA_VERSION = 22L
}
/**
@ -397,6 +398,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private fun migrateTo20(realm: DynamicRealm) {
Timber.d("Step 19 -> 20")
realm.schema.get("ChunkEntity")?.apply {
if (hasField("numberOfTimelineEvents")) {
removeField("numberOfTimelineEvents")
@ -419,6 +421,34 @@ internal class RealmSessionStoreMigration @Inject constructor(
private fun migrateTo21(realm: DynamicRealm) {
Timber.d("Step 20 -> 21")
realm.schema.get("RoomSummaryEntity")
?.addField(RoomSummaryEntityFields.E2E_ALGORITHM, String::class.java)
?.transform { obj ->
val encryptionContentAdapter = MoshiProvider.providesMoshi().adapter(EncryptionEventContent::class.java)
val encryptionEvent = realm.where("CurrentStateEventEntity")
.equalTo(CurrentStateEventEntityFields.ROOM_ID, obj.getString(RoomSummaryEntityFields.ROOM_ID))
.equalTo(CurrentStateEventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION)
.findFirst()
val encryptionEventRoot = encryptionEvent?.getObject(CurrentStateEventEntityFields.ROOT.`$`)
val algorithm = encryptionEventRoot
?.getString(EventEntityFields.CONTENT)?.let {
encryptionContentAdapter.fromJson(it)?.algorithm
}
obj.setString(RoomSummaryEntityFields.E2E_ALGORITHM, algorithm)
obj.setBoolean(RoomSummaryEntityFields.IS_ENCRYPTED, encryptionEvent != null)
encryptionEventRoot?.getLong(EventEntityFields.ORIGIN_SERVER_TS)?.let {
obj.setLong(RoomSummaryEntityFields.ENCRYPTION_EVENT_TS, it)
}
}
}
private fun migrateTo21(realm: DynamicRealm) {
Timber.d("Step 21 -> 22")
val eventEntity = realm.schema.get("TimelineEventEntity") ?: return
realm.schema.get("EventEntity")

View file

@ -30,8 +30,8 @@ internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long {
}
internal fun TimelineEventEntity.isMoreRecentThan(eventToCheck: TimelineEventEntity): Boolean {
val currentChunk = this.chunk?.first() ?: return false
val chunkToCheck = eventToCheck.chunk?.firstOrNull() ?: return false
val currentChunk = this.chunk?.first(null) ?: return false
val chunkToCheck = eventToCheck.chunk?.first(null) ?: return false
return if (currentChunk == chunkToCheck) {
this.displayIndex >= eventToCheck.displayIndex
} else {

View file

@ -16,12 +16,15 @@
package org.matrix.android.sdk.internal.database.mapper
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
import org.matrix.android.sdk.api.session.typing.TypingUsersTracker
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.presence.toUserPresence
import javax.inject.Inject
@ -68,7 +71,9 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
isEncrypted = roomSummaryEntity.isEncrypted,
encryptionEventTs = roomSummaryEntity.encryptionEventTs,
breadcrumbsIndex = roomSummaryEntity.breadcrumbsIndex,
roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel,
roomEncryptionTrustLevel = if (roomSummaryEntity.isEncrypted && roomSummaryEntity.e2eAlgorithm != MXCRYPTO_ALGORITHM_MEGOLM) {
RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm
} else roomSummaryEntity.roomEncryptionTrustLevel,
inviterId = roomSummaryEntity.inviterId,
hasFailedSending = roomSummaryEntity.hasFailedSending,
roomType = roomSummaryEntity.roomType,
@ -99,7 +104,13 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
worldReadable = it.childSummaryEntity?.joinRules == RoomJoinRules.PUBLIC
)
},
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList()
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList(),
roomEncryptionAlgorithm = when (val alg = roomSummaryEntity.e2eAlgorithm) {
// I should probably use #hasEncryptorClassForAlgorithm but it says it supports
// OLM which is some legacy? Now only megolm allowed in rooms
MXCRYPTO_ALGORITHM_MEGOLM -> RoomEncryptionAlgorithm.Megolm
else -> RoomEncryptionAlgorithm.UnsupportedAlgorithm(alg)
}
)
}
}

View file

@ -205,6 +205,11 @@ internal open class RoomSummaryEntity(
if (value != field) field = value
}
var e2eAlgorithm: String? = null
set(value) {
if (value != field) field = value
}
var encryptionEventTs: Long? = 0
set(value) {
if (value != field) field = value

View file

@ -119,15 +119,15 @@ internal class DefaultRoom(override val roomId: String,
}
}
override suspend fun enableEncryption(algorithm: String) {
override suspend fun enableEncryption(algorithm: String, force: Boolean) {
when {
isEncrypted() -> {
(!force && isEncrypted() && encryptionAlgorithm() == MXCRYPTO_ALGORITHM_MEGOLM) -> {
throw IllegalStateException("Encryption is already enabled for this room")
}
algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> {
(!force && algorithm != MXCRYPTO_ALGORITHM_MEGOLM) -> {
throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported")
}
else -> {
else -> {
val params = SendStateTask.Params(
roomId = roomId,
stateKey = null,

View file

@ -51,7 +51,7 @@ internal class DefaultRoomGetter @Inject constructor(
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
.findAll()
.firstOrNull { dm -> dm.otherMemberIds.size == 1 && dm.otherMemberIds.first() == otherUserId }
.firstOrNull { dm -> dm.otherMemberIds.size == 1 && dm.otherMemberIds.first(null) == otherUserId }
?.roomId
}
}

View file

@ -38,13 +38,11 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary
import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications
import org.matrix.android.sdk.internal.crypto.EventDecryptor
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
@ -57,7 +55,6 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.getOrNull
import org.matrix.android.sdk.internal.database.query.isEventRead
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.whereType
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.clearWith
import org.matrix.android.sdk.internal.query.process
@ -123,10 +120,8 @@ internal class RoomSummaryUpdater @Inject constructor(
Timber.v("## Space: Updating summary room [$roomId] roomType: [$roomType]")
// Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room
val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
.isNotNull(EventEntityFields.STATE_KEY)
.findFirst()
val encryptionEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ENCRYPTION, stateKey = "")?.root
Timber.v("## CRYPTO: currentEncryptionEvent is $encryptionEvent")
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
@ -152,6 +147,11 @@ internal class RoomSummaryUpdater @Inject constructor(
.orEmpty()
roomSummaryEntity.updateAliases(roomAliases)
roomSummaryEntity.isEncrypted = encryptionEvent != null
roomSummaryEntity.e2eAlgorithm = ContentMapper.map(encryptionEvent?.content)
?.toModel<EncryptionEventContent>()
?.algorithm
roomSummaryEntity.encryptionEventTs = encryptionEvent?.originServerTs
if (roomSummaryEntity.membership == Membership.INVITE && inviterId != null) {

View file

@ -473,9 +473,9 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
if (initialEventId != null) {
frozenTimelineEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, initialEventId).findFirst()?.displayIndex
} else if (direction == Timeline.Direction.BACKWARDS) {
frozenTimelineEvents.first()?.displayIndex
frozenTimelineEvents.first(null)?.displayIndex
} else {
frozenTimelineEvents.last()?.displayIndex
frozenTimelineEvents.last(null)?.displayIndex
}
} else if (direction == Timeline.Direction.FORWARDS) {
builtEvents.first().displayIndex + 1

View file

@ -224,6 +224,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
}
val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it }
val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType)
Timber.v("## received state event ${event.type} and key ${event.stateKey}")
CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
// Timber.v("## Space state event: $eventEntity")
eventId = event.eventId
@ -399,6 +400,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
roomMemberEventHandler.handle(realm, roomEntity.roomId, event.stateKey, fixedContent, aggregator)
}
}
roomMemberContentsByUser.getOrPut(event.senderId) {
// If we don't have any new state on this user, get it from db
val rootStateEvent = CurrentStateEventEntity.getOrNull(realm, roomId, event.senderId, EventType.STATE_ROOM_MEMBER)?.root

View file

@ -15,7 +15,10 @@ kapt {
// Note: 2 digits max for each value
ext.versionMajor = 1
ext.versionMinor = 3
ext.versionPatch = 13
// Note: even values are reserved for regular release, odd values for hotfix release.
// When creating a hotfix, you should decrease the value, since the current value
// is the value for the next regular release.
ext.versionPatch = 16
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
@ -207,7 +210,6 @@ android {
// Comment to run on Android 12
// execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
signingConfigs {
debug {
keyAlias 'androiddebugkey'
@ -215,6 +217,12 @@ android {
storeFile file('./signature/debug.keystore')
storePassword 'android'
}
release {
keyAlias project.property("signing.element.keyId")
keyPassword project.property("signing.element.keyPassword")
storeFile file(project.property("signing.element.storePath"))
storePassword project.property("signing.element.storePassword")
}
}
buildTypes {
@ -245,6 +253,7 @@ android {
optimizeCode true
proguardFiles 'proguard-rules.pro'
}
signingConfig signingConfigs.release
}
}
@ -362,7 +371,7 @@ dependencies {
implementation 'com.facebook.stetho:stetho:1.6.0'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.40'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41'
// FlowBinding
implementation libs.github.flowBinding

View file

@ -154,8 +154,6 @@ class SecurityBootstrapTest : VerificationTestBase() {
onView(withId(R.id.recoveryCopy))
.perform(click())
Thread.sleep(1000)
// Dismiss dialog
onView(withText(R.string.ok)).inRoot(RootMatchers.isDialog()).perform(click())

View file

@ -24,6 +24,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils
@ -38,7 +39,7 @@ import im.vector.app.features.themes.ThemeUtils
abstract class GenericFooterItem : VectorEpoxyModel<GenericFooterItem.Holder>() {
@EpoxyAttribute
var text: String? = null
var text: EpoxyCharSequence? = null
@EpoxyAttribute
var style: ItemStyle = ItemStyle.NORMAL_TEXT
@ -56,7 +57,7 @@ abstract class GenericFooterItem : VectorEpoxyModel<GenericFooterItem.Holder>()
override fun bind(holder: Holder) {
super.bind(holder)
holder.text.setTextOrHide(text)
holder.text.setTextOrHide(text?.charSequence)
holder.text.typeface = style.toTypeFace()
holder.text.textSize = style.toTextSize()
holder.text.gravity = if (centered) Gravity.CENTER_HORIZONTAL else Gravity.START

View file

@ -25,6 +25,7 @@ import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import androidx.core.text.italic
import im.vector.app.R
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.error.ResourceLimitErrorFormatter
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.utils.DimensionConverter
@ -73,6 +74,7 @@ class NotificationAreaView @JvmOverloads constructor(
is State.Default -> renderDefault()
is State.Hidden -> renderHidden()
is State.NoPermissionToPost -> renderNoPermissionToPost()
is State.UnsupportedAlgorithm -> renderUnsupportedAlgorithm(newState)
is State.Tombstone -> renderTombstone()
is State.ResourceLimitExceededError -> renderResourceLimitExceededError(newState)
}.exhaustive
@ -106,6 +108,24 @@ class NotificationAreaView @JvmOverloads constructor(
views.roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary))
}
private fun renderUnsupportedAlgorithm(e2eState: State.UnsupportedAlgorithm) {
visibility = View.VISIBLE
views.roomNotificationIcon.setImageResource(R.drawable.ic_warning_badge)
val text = if (e2eState.canRestore) {
R.string.room_unsupported_e2e_algorithm_as_admin
} else R.string.room_unsupported_e2e_algorithm
val message = span {
italic {
+resources.getString(text)
}
}
views.roomNotificationMessage.onClick {
delegate?.onMisconfiguredEncryptionClicked()
}
views.roomNotificationMessage.text = message
views.roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary))
}
private fun renderResourceLimitExceededError(state: State.ResourceLimitExceededError) {
visibility = View.VISIBLE
val resourceLimitErrorFormatter = ResourceLimitErrorFormatter(context)
@ -163,6 +183,7 @@ class NotificationAreaView @JvmOverloads constructor(
// User can't post messages to room because his power level doesn't allow it.
object NoPermissionToPost : State()
data class UnsupportedAlgorithm(val canRestore: Boolean) : State()
// View will be Gone
object Hidden : State()
@ -179,5 +200,6 @@ class NotificationAreaView @JvmOverloads constructor(
*/
interface Delegate {
fun onTombstoneEventClicked()
fun onMisconfiguredEncryptionClicked()
}
}

View file

@ -61,6 +61,10 @@ class ShieldImageView @JvmOverloads constructor(
else R.drawable.ic_shield_trusted
)
}
RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> {
contentDescription = context.getString(R.string.a11y_trust_level_trusted)
setImageResource(R.drawable.ic_warning_badge)
}
}
}
}
@ -71,5 +75,6 @@ fun RoomEncryptionTrustLevel.toDrawableRes(): Int {
RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge
}
}

View file

@ -18,6 +18,7 @@ package im.vector.app.features.devtools
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.features.form.formEditTextItem
@ -36,7 +37,7 @@ class RoomDevToolSendFormController @Inject constructor(
genericFooterItem {
id("topSpace")
text("")
text("".toEpoxyCharSequence())
}
formEditTextItem {
id("event_type")

View file

@ -42,6 +42,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
object MarkAllAsRead : RoomDetailAction()
data class DownloadOrOpen(val eventId: String, val senderId: String?, val messageFileContent: MessageWithAttachmentContent) : RoomDetailAction()
object JoinAndOpenReplacementRoom : RoomDetailAction()
object OnClickMisconfiguredEncryption : RoomDetailAction()
object AcceptInvite : RoomDetailAction()
object RejectInvite : RoomDetailAction()

View file

@ -48,6 +48,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
object OpenInvitePeople : RoomDetailViewEvents()
object OpenSetRoomAvatarDialog : RoomDetailViewEvents()
object OpenRoomSettings : RoomDetailViewEvents()
object OpenRoomProfile : RoomDetailViewEvents()
data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents()
object ShowWaitingView : RoomDetailViewEvents()

View file

@ -226,11 +226,13 @@ class RoomDetailViewModel @AssistedInject constructor(
val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId)
val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId)
val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE)
val isAllowedToSetupEncryption = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
setState {
copy(
canInvite = canInvite,
isAllowedToManageWidgets = isAllowedToManageWidgets,
isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall
isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall,
isAllowedToSetupEncryption = isAllowedToSetupEncryption
)
}
}.launchIn(viewModelScope)
@ -355,6 +357,7 @@ class RoomDetailViewModel @AssistedInject constructor(
is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action)
is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action)
is RoomDetailAction.JoinAndOpenReplacementRoom -> handleJoinAndOpenReplacementRoom()
is RoomDetailAction.OnClickMisconfiguredEncryption -> handleClickMisconfiguredE2E()
is RoomDetailAction.ResendMessage -> handleResendEvent(action)
is RoomDetailAction.RemoveFailedEcho -> handleRemove(action)
is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead()
@ -663,6 +666,12 @@ class RoomDetailViewModel @AssistedInject constructor(
}
}
private fun handleClickMisconfiguredE2E() = withState { state ->
if (state.isAllowedToSetupEncryption) {
_viewEvents.post(RoomDetailViewEvents.OpenRoomProfile)
}
}
private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()
fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->

View file

@ -66,6 +66,7 @@ data class RoomDetailViewState(
val canInvite: Boolean = true,
val isAllowedToManageWidgets: Boolean = false,
val isAllowedToStartWebRTCCall: Boolean = true,
val isAllowedToSetupEncryption: Boolean = true,
val hasFailedSending: Boolean = false,
val jitsiState: JitsiState = JitsiState(),
val rootThreadEventId: String? = null,

View file

@ -136,12 +136,14 @@ import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivit
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.composer.CanSendStatus
import im.vector.app.features.home.room.detail.composer.MessageComposerAction
import im.vector.app.features.home.room.detail.composer.MessageComposerView
import im.vector.app.features.home.room.detail.composer.MessageComposerViewEvents
import im.vector.app.features.home.room.detail.composer.MessageComposerViewModel
import im.vector.app.features.home.room.detail.composer.MessageComposerViewState
import im.vector.app.features.home.room.detail.composer.SendMode
import im.vector.app.features.home.room.detail.composer.boolean
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.RecordingUiState
import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
@ -391,7 +393,7 @@ class TimelineFragment @Inject constructor(
}
messageComposerViewModel.onEach(MessageComposerViewState::sendMode, MessageComposerViewState::canSendMessage) { mode, canSend ->
if (!canSend) {
if (!canSend.boolean()) {
return@onEach
}
when (mode) {
@ -458,7 +460,8 @@ class TimelineFragment @Inject constructor(
is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)
RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), timelineArgs.roomId)
RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show()
RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings()
RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings(RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS)
RoomDetailViewEvents.OpenRoomProfile -> handleOpenRoomSettings()
is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
navigator.openBigImageViewer(requireActivity(), it.view, item)
}
@ -582,11 +585,11 @@ class TimelineFragment @Inject constructor(
)
}
private fun handleOpenRoomSettings() {
private fun handleOpenRoomSettings(directAccess: Int? = null) {
navigator.openRoomProfile(
requireContext(),
timelineArgs.roomId,
RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS
roomDetailArgs.roomId,
directAccess
)
}
@ -951,6 +954,10 @@ class TimelineFragment @Inject constructor(
override fun onTombstoneEventClicked() {
roomDetailViewModel.handle(RoomDetailAction.JoinAndOpenReplacementRoom)
}
override fun onMisconfiguredEncryptionClicked() {
roomDetailViewModel.handle(RoomDetailAction.OnClickMisconfiguredEncryption)
}
}
}
@ -1348,7 +1355,7 @@ class TimelineFragment @Inject constructor(
val canSendMessage = withState(messageComposerViewModel) {
it.canSendMessage
}
if (!canSendMessage) {
if (!canSendMessage.boolean()) {
return false
}
return when (model) {
@ -1530,10 +1537,18 @@ class TimelineFragment @Inject constructor(
views.voiceMessageRecorderView.render(messageComposerState.voiceRecordingUiState)
views.composerLayout.setRoomEncrypted(summary.isEncrypted)
// views.composerLayout.alwaysShowSendButton = false
if (messageComposerState.canSendMessage) {
views.notificationAreaView.render(NotificationAreaView.State.Hidden)
} else {
views.notificationAreaView.render(NotificationAreaView.State.NoPermissionToPost)
when (messageComposerState.canSendMessage) {
CanSendStatus.Allowed -> {
NotificationAreaView.State.Hidden
}
CanSendStatus.NoPermission -> {
NotificationAreaView.State.NoPermissionToPost
}
is CanSendStatus.UnSupportedE2eAlgorithm -> {
NotificationAreaView.State.UnsupportedAlgorithm(mainState.isAllowedToSetupEncryption)
}
}.let {
views.notificationAreaView.render(it)
}
} else {
views.hideComposerViews()

View file

@ -38,6 +38,7 @@ import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.voice.VoicePlayerHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
@ -47,6 +48,7 @@ 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.RoomAvatarContent
import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
@ -55,6 +57,8 @@ import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap
import timber.log.Timber
class MessageComposerViewModel @AssistedInject constructor(
@ -74,7 +78,7 @@ class MessageComposerViewModel @AssistedInject constructor(
init {
loadDraftIfAny()
observePowerLevel()
observePowerLevelAndEncryption()
subscribeToStateInternal()
}
@ -137,12 +141,30 @@ class MessageComposerViewModel @AssistedInject constructor(
}
}
private fun observePowerLevel() {
PowerLevelsFlowFactory(room).createFlow()
.setOnEach {
val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
copy(canSendMessage = canSendMessage)
private fun observePowerLevelAndEncryption() {
combine(
PowerLevelsFlowFactory(room).createFlow(),
room.flow().liveRoomSummary().unwrap()
) { pl, sum ->
val canSendMessage = PowerLevelsHelper(pl).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
if (canSendMessage) {
val isE2E = sum.isEncrypted
if (isE2E) {
val roomEncryptionAlgorithm = sum.roomEncryptionAlgorithm
if (roomEncryptionAlgorithm is RoomEncryptionAlgorithm.UnsupportedAlgorithm) {
CanSendStatus.UnSupportedE2eAlgorithm(roomEncryptionAlgorithm.name)
} else {
CanSendStatus.Allowed
}
} else {
CanSendStatus.Allowed
}
} else {
CanSendStatus.NoPermission
}
}.setOnEach {
copy(canSendMessage = it)
}
}
private fun handleEnterQuoteMode(action: MessageComposerAction.EnterQuoteMode) {

View file

@ -43,9 +43,23 @@ sealed interface SendMode {
data class Voice(val text: String) : SendMode
}
sealed interface CanSendStatus {
object Allowed : CanSendStatus
object NoPermission : CanSendStatus
data class UnSupportedE2eAlgorithm(val algorithm: String?) : CanSendStatus
}
fun CanSendStatus.boolean(): Boolean {
return when (this) {
CanSendStatus.Allowed -> true
CanSendStatus.NoPermission -> false
is CanSendStatus.UnSupportedE2eAlgorithm -> false
}
}
data class MessageComposerViewState(
val roomId: String,
val canSendMessage: Boolean = true,
val canSendMessage: CanSendStatus = CanSendStatus.Allowed,
val isSendButtonVisible: Boolean = false,
val rootThreadEventId: String? = null,
val sendMode: SendMode = SendMode.Regular("", false),
@ -60,8 +74,9 @@ data class MessageComposerViewState(
}
val isVoiceMessageIdle = !isVoiceRecording
val isComposerVisible = canSendMessage && !isVoiceRecording
val isVoiceMessageRecorderVisible = canSendMessage && !isSendButtonVisible
val isComposerVisible = canSendMessage.boolean() && !isVoiceRecording
val isVoiceMessageRecorderVisible = canSendMessage.boolean() && !isSendButtonVisible
constructor(args: TimelineArgs) : this(
roomId = args.roomId,

View file

@ -62,7 +62,7 @@ class ViewEditHistoryEpoxyController @Inject constructor(
is Fail -> {
genericFooterItem {
id("failure")
text(host.stringProvider.getString(R.string.unknown_error))
text(host.stringProvider.getString(R.string.unknown_error).toEpoxyCharSequence())
}
}
is Success -> {

View file

@ -63,9 +63,9 @@ class EncryptionItemFactory @Inject constructor(
)
shield = StatusTileTimelineItem.ShieldUIState.BLACK
} else {
title = stringProvider.getString(R.string.encryption_not_enabled)
title = stringProvider.getString(R.string.encryption_misconfigured)
description = stringProvider.getString(R.string.encryption_unknown_algorithm_tile_description)
shield = StatusTileTimelineItem.ShieldUIState.RED
shield = StatusTileTimelineItem.ShieldUIState.ERROR
}
return StatusTileTimelineItem_()
.attributes(

View file

@ -57,6 +57,7 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItem<StatusTileTimelineIte
ShieldUIState.GREEN -> R.drawable.ic_shield_trusted
ShieldUIState.BLACK -> R.drawable.ic_shield_black
ShieldUIState.RED -> R.drawable.ic_shield_warning
ShieldUIState.ERROR -> R.drawable.ic_warning_badge
}
holder.titleView.setCompoundDrawablesWithIntrinsicBounds(
@ -98,6 +99,7 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItem<StatusTileTimelineIte
enum class ShieldUIState {
BLACK,
RED,
GREEN
GREEN,
ERROR
}
}

View file

@ -49,7 +49,7 @@ class ViewReactionsEpoxyController @Inject constructor(
is Fail -> {
genericFooterItem {
id("failure")
text(host.stringProvider.getString(R.string.unknown_error))
text(host.stringProvider.getString(R.string.unknown_error).toEpoxyCharSequence())
}
}
is Success -> {

View file

@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.widget
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericButtonItem
@ -40,7 +41,7 @@ class RoomWidgetsController @Inject constructor(
if (widgets.isEmpty()) {
genericFooterItem {
id("empty")
text(host.stringProvider.getString(R.string.room_no_active_widgets))
text(host.stringProvider.getString(R.string.room_no_active_widgets).toEpoxyCharSequence())
}
} else {
widgets.forEach { widget ->

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