mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Merge branch 'release/1.4.18' into main
This commit is contained in:
commit
ba5828711b
4
.github/workflows/post-pr.yml
vendored
4
.github/workflows/post-pr.yml
vendored
@ -325,5 +325,5 @@ jobs:
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
hookshot_url: ${{ secrets.ELEMENT_ANDROID_HOOKSHOT_URL }}
|
||||
text_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
|
||||
html_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"
|
||||
text_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by.login }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
|
||||
html_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by.login }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"
|
||||
|
24
.github/workflows/quality.yml
vendored
24
.github/workflows/quality.yml
vendored
@ -5,6 +5,11 @@ on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
CI_GRADLE_ARG_PROPERTIES: >
|
||||
-Porg.gradle.jvmargs=-Xmx4g
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Project Check Suite
|
||||
@ -97,6 +102,25 @@ jobs:
|
||||
comment_id: ${{ steps.fc.outputs.comment-id }}
|
||||
})
|
||||
|
||||
# Gradle dependency analysis using https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin
|
||||
dependency-analysis:
|
||||
name: Dependency analysis
|
||||
runs-on: ubuntu-latest
|
||||
# Allow all jobs on main and develop. Just one per PR.
|
||||
concurrency:
|
||||
group: ${{ github.ref == 'refs/heads/main' && format('dep-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('dep-develop-{0}', github.sha) || format('dep-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Dependency analysis
|
||||
run: ./gradlew buildHealth $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload dependency analysis
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dependency-analysis
|
||||
path: build/reports/dependency-analysis/build-health-report.txt
|
||||
|
||||
# Lint for main module
|
||||
android-lint:
|
||||
name: Android Linter
|
||||
|
66
CHANGES.md
66
CHANGES.md
@ -1,3 +1,67 @@
|
||||
Changes in Element v1.4.18 (2022-05-31)
|
||||
=======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Space explore screen changes: removed space card, added rooms filtering ([#5658](https://github.com/vector-im/element-android/issues/5658))
|
||||
- Adds space or user id as a subtitle under rooms in search ([#5860](https://github.com/vector-im/element-android/issues/5860))
|
||||
- Adds up navigation in spaces ([#6073](https://github.com/vector-im/element-android/issues/6073))
|
||||
- Labs flag for enabling live location sharing ([#6098](https://github.com/vector-im/element-android/issues/6098))
|
||||
- Added support for mandatory backup or passphrase from .well-known configuration. ([#6133](https://github.com/vector-im/element-android/issues/6133))
|
||||
- Security - Asking for user confirmation when tapping URLs which contain unicode directional overrides ([#6163](https://github.com/vector-im/element-android/issues/6163))
|
||||
- Add settings switch to allow autoplaying animated images ([#6166](https://github.com/vector-im/element-android/issues/6166))
|
||||
- Live Location Sharing - User List Bottom Sheet ([#6170](https://github.com/vector-im/element-android/issues/6170))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Fix some notifications not clearing when read ([#4862](https://github.com/vector-im/element-android/issues/4862))
|
||||
- Do not switch away from home space on notification when "Show all Rooms in Home" is selected. ([#5827](https://github.com/vector-im/element-android/issues/5827))
|
||||
- Use fixed text size in read receipt counter ([#5856](https://github.com/vector-im/element-android/issues/5856))
|
||||
- Revert: Use member name instead of room name in DM creation item ([#6032](https://github.com/vector-im/element-android/issues/6032))
|
||||
- Poll refactoring with unit tests ([#6074](https://github.com/vector-im/element-android/issues/6074))
|
||||
- Correct .well-known/matrix/client handling for server_names which include ports. ([#6095](https://github.com/vector-im/element-android/issues/6095))
|
||||
- Glide - Use current drawable while loading new static map image ([#6103](https://github.com/vector-im/element-android/issues/6103))
|
||||
- Fix sending multiple invites to a room reaching only one or two people ([#6109](https://github.com/vector-im/element-android/issues/6109))
|
||||
- Prevent widget web view from reloading on screen / orientation change ([#6140](https://github.com/vector-im/element-android/issues/6140))
|
||||
- Fix decrypting redacted event from sending errors ([#6148](https://github.com/vector-im/element-android/issues/6148))
|
||||
- Make widget web view request system permissions for camera and microphone (PSF-1061) ([#6149](https://github.com/vector-im/element-android/issues/6149))
|
||||
|
||||
In development 🚧
|
||||
----------------
|
||||
- Adds email input and verification screens to the new FTUE onboarding flow ([#5278](https://github.com/vector-im/element-android/issues/5278))
|
||||
- FTUE - Adds the redesigned Sign In screen ([#5283](https://github.com/vector-im/element-android/issues/5283))
|
||||
- [Live location sharing] Update message in timeline during the live ([#5689](https://github.com/vector-im/element-android/issues/5689))
|
||||
- FTUE - Overrides sign up flow ordering for matrix.org only ([#5783](https://github.com/vector-im/element-android/issues/5783))
|
||||
- Live location sharing: navigation from timeline to map screen
|
||||
Live location sharing: show user pins on map screen ([#6012](https://github.com/vector-im/element-android/issues/6012))
|
||||
- FTUE - Adds homeserver login/register deeplink support ([#6023](https://github.com/vector-im/element-android/issues/6023))
|
||||
- [Live location sharing] Update entity in DB when a live is timed out ([#6123](https://github.com/vector-im/element-android/issues/6123))
|
||||
|
||||
SDK API changes ⚠️
|
||||
------------------
|
||||
- Notifies other devices when a verification request sent from an Android device is accepted.` ([#5724](https://github.com/vector-im/element-android/issues/5724))
|
||||
- Some `val` have been changed to `fun` to increase their visibility in the generated documentation. Just add `()` if you were using them.
|
||||
- `KeysBackupService.state` has been replaced by `KeysBackupService.getState()`
|
||||
- `KeysBackupService.isStucked` has been replaced by `KeysBackupService.isStuck()`
|
||||
- SDK documentation improved ([#5952](https://github.com/vector-im/element-android/issues/5952))
|
||||
- Improve replay attacks and reduce duplicate message index errors ([#6077](https://github.com/vector-im/element-android/issues/6077))
|
||||
- Remove `RoomSummaryQueryParams.roomId`. If you need to observe a single room, use the new API `RoomService.getRoomSummaryLive(roomId: String)`
|
||||
- `ActiveSpaceFilter` has been renamed to `SpaceFilter`
|
||||
- `RoomCategoryFilter.ALL` has been removed, just pass `null` to not filter on Room category. ([#6143](https://github.com/vector-im/element-android/issues/6143))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- leaving space experience changed to be aligned with iOS ([#5728](https://github.com/vector-im/element-android/issues/5728))
|
||||
- @Ignore a number of tests that are currently failing in CI. ([#6025](https://github.com/vector-im/element-android/issues/6025))
|
||||
- Remove ShortcutBadger lib and usage (it was dead code) ([#6041](https://github.com/vector-im/element-android/issues/6041))
|
||||
- Test: Ensure calling 'fail()' is not caught by the catch block ([#6089](https://github.com/vector-im/element-android/issues/6089))
|
||||
- Excludes transitive optional non FOSS google location dependency from fdroid builds ([#6100](https://github.com/vector-im/element-android/issues/6100))
|
||||
- Fixed grammar errors in /vector/src/main/res/values/strings.xml ([#6132](https://github.com/vector-im/element-android/issues/6132))
|
||||
- Downgrade gradle from 7.2.0 to 7.1.3 ([#6141](https://github.com/vector-im/element-android/issues/6141))
|
||||
- Add Lao language to the in-app settings. ([#6196](https://github.com/vector-im/element-android/issues/6196))
|
||||
- Remove the background location permission request ([#6198](https://github.com/vector-im/element-android/issues/6198))
|
||||
|
||||
|
||||
Changes in Element v1.4.16 (2022-05-17)
|
||||
=======================================
|
||||
|
||||
@ -36,7 +100,7 @@ Other changes
|
||||
- Reformatted project code ([#5953](https://github.com/vector-im/element-android/issues/5953))
|
||||
- Update check for server-side threads support to match spec. ([#5997](https://github.com/vector-im/element-android/issues/5997))
|
||||
- Setup detekt ([#6038](https://github.com/vector-im/element-android/issues/6038))
|
||||
- Notify the user for each new message ([#46312](https://github.com/vector-im/element-android/issues/46312))
|
||||
- Notify the user for each new message ([#4632](https://github.com/vector-im/element-android/issues/4632))
|
||||
|
||||
|
||||
Changes in Element v1.4.14 (2022-05-05)
|
||||
|
63
build.gradle
63
build.gradle
@ -27,7 +27,7 @@ buildscript {
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
|
||||
classpath "com.likethesalad.android:stem-plugin:2.0.0"
|
||||
classpath "com.likethesalad.android:stem-plugin:2.1.1"
|
||||
classpath 'org.owasp:dependency-check-gradle:7.1.0.1'
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21"
|
||||
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
||||
@ -41,6 +41,9 @@ plugins {
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.3.0"
|
||||
// Detekt
|
||||
id "io.gitlab.arturbosch.detekt" version "1.20.0"
|
||||
|
||||
// Dependency Analysis
|
||||
id 'com.autonomousapps.dependency-analysis' version "1.4.0"
|
||||
}
|
||||
|
||||
// https://github.com/jeremylong/DependencyCheck
|
||||
@ -219,3 +222,61 @@ project(":library:diff-match-patch") {
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
dependencyAnalysis {
|
||||
dependencies {
|
||||
bundle("kotlin-stdlib") {
|
||||
includeGroup("org.jetbrains.kotlin")
|
||||
}
|
||||
bundle("react") {
|
||||
includeGroup("com.facebook.react")
|
||||
}
|
||||
}
|
||||
issues {
|
||||
all {
|
||||
ignoreKtx(true)
|
||||
onUsedTransitiveDependencies {
|
||||
// Transitively used dependencies that should be declared directly
|
||||
severity("ignore")
|
||||
}
|
||||
onUnusedDependencies {
|
||||
severity("fail")
|
||||
}
|
||||
onUnusedAnnotationProcessors {
|
||||
severity("fail")
|
||||
exclude("com.airbnb.android:epoxy-processor", "com.google.dagger:hilt-compiler") // False positives
|
||||
}
|
||||
}
|
||||
project(":library:jsonviewer") {
|
||||
onUnusedDependencies {
|
||||
exclude("org.json:json") // Used in unit tests, overwrites the one bundled into Android
|
||||
}
|
||||
}
|
||||
project(":library:ui-styles") {
|
||||
onUnusedDependencies {
|
||||
exclude("com.github.vector-im:PFLockScreen-Android") // False positive
|
||||
}
|
||||
}
|
||||
project(":matrix-sdk-android") {
|
||||
onUnusedDependencies {
|
||||
exclude("io.reactivex.rxjava2:rxkotlin") // Transitively required for mocking realm as monarchy doesn't expose Rx
|
||||
}
|
||||
}
|
||||
project(":matrix-sdk-android-flow") {
|
||||
onUnusedDependencies {
|
||||
exclude("androidx.paging:paging-runtime-ktx") // False positive
|
||||
}
|
||||
}
|
||||
project(":vector") {
|
||||
onUnusedDependencies {
|
||||
// False positives
|
||||
exclude(
|
||||
"com.vanniktech:emoji-google",
|
||||
"com.vanniktech:emoji-material",
|
||||
"org.maplibre.gl:android-plugin-annotation-v9",
|
||||
"org.maplibre.gl:android-sdk",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,13 @@ ext.versions = [
|
||||
'targetCompat' : JavaVersion.VERSION_11,
|
||||
]
|
||||
|
||||
def gradle = "7.2.0"
|
||||
|
||||
// Pinned to 7.1.3 because of https://github.com/vector-im/element-android/issues/6142
|
||||
// Please test carefully before upgrading again.
|
||||
def gradle = "7.1.3"
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
def kotlin = "1.6.21"
|
||||
def kotlinCoroutines = "1.6.1"
|
||||
def kotlinCoroutines = "1.6.2"
|
||||
def dagger = "2.42"
|
||||
def retrofit = "2.9.0"
|
||||
def arrow = "0.8.2"
|
||||
@ -23,7 +26,7 @@ def mavericks = "2.6.1"
|
||||
def glide = "4.13.2"
|
||||
def bigImageViewer = "1.8.1"
|
||||
def jjwt = "0.11.5"
|
||||
def vanniktechEmoji = "0.9.0"
|
||||
def vanniktechEmoji = "0.15.0"
|
||||
|
||||
// Testing
|
||||
def mockk = "1.12.4"
|
||||
@ -45,12 +48,13 @@ ext.libs = [
|
||||
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
|
||||
],
|
||||
androidx : [
|
||||
'activity' : "androidx.activity:activity:1.4.0",
|
||||
'appCompat' : "androidx.appcompat:appcompat:1.4.1",
|
||||
'core' : "androidx.core:core-ktx:1.7.0",
|
||||
'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1",
|
||||
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3",
|
||||
'fragmentKtx' : "androidx.fragment:fragment-ktx:1.4.1",
|
||||
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.3",
|
||||
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.4",
|
||||
'work' : "androidx.work:work-runtime-ktx:2.7.1",
|
||||
'autoFill' : "androidx.autofill:autofill:1.1.0",
|
||||
'preferenceKtx' : "androidx.preference:preference-ktx:1.2.0",
|
||||
@ -69,7 +73,9 @@ ext.libs = [
|
||||
'testRules' : "androidx.test:rules:$androidxTest",
|
||||
'espressoCore' : "androidx.test.espresso:espresso-core:$espresso",
|
||||
'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso",
|
||||
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso"
|
||||
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso",
|
||||
'viewpager2' : "androidx.viewpager2:viewpager2:1.0.0",
|
||||
'transition' : "androidx.transition:transition:1.2.0",
|
||||
],
|
||||
google : [
|
||||
'material' : "com.google.android.material:material:1.6.0"
|
||||
@ -81,7 +87,7 @@ ext.libs = [
|
||||
'hiltCompiler' : "com.google.dagger:hilt-compiler:$dagger"
|
||||
],
|
||||
squareup : [
|
||||
'moshi' : "com.squareup.moshi:moshi-adapters:$moshi",
|
||||
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
||||
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
|
||||
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
|
||||
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
|
||||
@ -107,6 +113,10 @@ ext.libs = [
|
||||
'mavericks' : "com.airbnb.android:mavericks:$mavericks",
|
||||
'mavericksTesting' : "com.airbnb.android:mavericks-testing:$mavericks"
|
||||
],
|
||||
maplibre : [
|
||||
'androidSdk' : "org.maplibre.gl:android-sdk:9.5.2",
|
||||
'pluginAnnotation' : "org.maplibre.gl:android-plugin-annotation-v9:1.0.0"
|
||||
],
|
||||
mockk : [
|
||||
'mockk' : "io.mockk:mockk:$mockk",
|
||||
'mockkAndroid' : "io.mockk:mockk-android:$mockk"
|
||||
|
@ -141,7 +141,6 @@ ext.groups = [
|
||||
'jline',
|
||||
'jp.wasabeef',
|
||||
'junit',
|
||||
'me.leolin',
|
||||
'me.saket',
|
||||
'net.bytebuddy',
|
||||
'net.java',
|
||||
|
@ -1,5 +1,34 @@
|
||||
# Adding and removing ThreePids to an account
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Add email](#add-email)
|
||||
* [User enter the email](#user-enter-the-email)
|
||||
* [The email is already added to an account](#the-email-is-already-added-to-an-account)
|
||||
* [The email is free](#the-email-is-free)
|
||||
* [User receives an e-mail](#user-receives-an-e-mail)
|
||||
* [User clicks on the link](#user-clicks-on-the-link)
|
||||
* [User returns on Element](#user-returns-on-element)
|
||||
* [User enters his password](#user-enters-his-password)
|
||||
* [The link has not been clicked](#the-link-has-not-been-clicked)
|
||||
* [Wrong password](#wrong-password)
|
||||
* [The link has been clicked and the account password is correct](#the-link-has-been-clicked-and-the-account-password-is-correct)
|
||||
* [Remove email](#remove-email)
|
||||
* [User want to remove an email from his account](#user-want-to-remove-an-email-from-his-account)
|
||||
* [Email was not bound to an identity server](#email-was-not-bound-to-an-identity-server)
|
||||
* [Email was bound to an identity server](#email-was-bound-to-an-identity-server)
|
||||
* [Add phone number](#add-phone-number)
|
||||
* [The phone number is already added to an account](#the-phone-number-is-already-added-to-an-account)
|
||||
* [The phone number is free](#the-phone-number-is-free)
|
||||
* [User receive a text message](#user-receive-a-text-message)
|
||||
* [User enter the code to the app](#user-enter-the-code-to-the-app)
|
||||
* [Wrong code](#wrong-code)
|
||||
* [Correct code](#correct-code)
|
||||
* [Remove phone number](#remove-phone-number)
|
||||
* [User wants to remove a phone number from his account](#user-wants-to-remove-a-phone-number-from-his-account)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Add email
|
||||
|
||||
### User enter the email
|
||||
|
@ -1,5 +1,13 @@
|
||||
# Analytics in Element
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Solution](#solution)
|
||||
* [How to add a new Event](#how-to-add-a-new-event)
|
||||
* [Forks of Element](#forks-of-element)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Solution
|
||||
|
||||
Element is using PostHog to send analytics event.
|
||||
|
@ -1,5 +1,14 @@
|
||||
# Color migration
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Changes](#changes)
|
||||
* [Main change for developers](#main-change-for-developers)
|
||||
* [Remaining work](#remaining-work)
|
||||
* [Migration guide](#migration-guide)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
### Changes
|
||||
|
||||
- use colors defined in https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=557%3A0
|
||||
|
@ -1,5 +1,31 @@
|
||||
# Element Android design
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [How to import from Figma to the Element Android project](#how-to-import-from-figma-to-the-element-android-project)
|
||||
* [Colors](#colors)
|
||||
* [Text](#text)
|
||||
* [Dimension, position and margin](#dimension-position-and-margin)
|
||||
* [Icons](#icons)
|
||||
* [Export drawable from Figma](#export-drawable-from-figma)
|
||||
* [Import in Android Studio](#import-in-android-studio)
|
||||
* [Images](#images)
|
||||
* [Figma links](#figma-links)
|
||||
* [Coumpound](#coumpound)
|
||||
* [Login](#login)
|
||||
* [Login v2](#login-v2)
|
||||
* [Room list](#room-list)
|
||||
* [Timeline](#timeline)
|
||||
* [Voice message](#voice-message)
|
||||
* [Room settings](#room-settings)
|
||||
* [VoIP](#voip)
|
||||
* [Presence](#presence)
|
||||
* [Spaces](#spaces)
|
||||
* [List to be continued...](#list-to-be-continued)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Introduction
|
||||
|
||||
Design at element.io is done using Figma - https://www.figma.com
|
||||
|
@ -1,5 +1,19 @@
|
||||
# Identity server
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Implementation](#implementation)
|
||||
* [Related MSCs](#related-mscs)
|
||||
* [Steps and requirements](#steps-and-requirements)
|
||||
* [Screens](#screens)
|
||||
* [Settings](#settings)
|
||||
* [Discovery screen](#discovery-screen)
|
||||
* [Set identity server screen](#set-identity-server-screen)
|
||||
* [Ref:](#ref:)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
Issue: #607
|
||||
PR: #1354
|
||||
|
||||
|
@ -1,5 +1,18 @@
|
||||
# Integration tests
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Pre requirements](#pre-requirements)
|
||||
* [Install and run Synapse](#install-and-run-synapse)
|
||||
* [Run the test](#run-the-test)
|
||||
* [Stop Synapse](#stop-synapse)
|
||||
* [Troubleshoot](#troubleshoot)
|
||||
* [Android Emulator does cannot reach the homeserver](#android-emulator-does-cannot-reach-the-homeserver)
|
||||
* [Tests partially run but some fail with "Unable to contact localhost:8080"](#tests-partially-run-but-some-fail-with-"unable-to-contact-localhost:8080")
|
||||
* [virtualenv command fails](#virtualenv-command-fails)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
Integration tests are useful to ensure that the code works well for any use cases.
|
||||
|
||||
They can also be used as sample on how to use the Matrix SDK.
|
||||
|
@ -1,20 +1,32 @@
|
||||
# Jitsi in Element Android
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Native Jitsi SDK](#native-jitsi-sdk)
|
||||
* [How to build the Jitsi Meet SDK](#how-to-build-the-jitsi-meet-sdk)
|
||||
* [Jitsi version](#jitsi-version)
|
||||
* [Run the build script](#run-the-build-script)
|
||||
* [Link with the new generated library](#link-with-the-new-generated-library)
|
||||
* [Sanity tests](#sanity-tests)
|
||||
* [Export the build library](#export-the-build-library)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
Native Jitsi support has been added to Element Android by the PR [#1914](https://github.com/vector-im/element-android/pull/1914). The description of the PR contains some documentation about the behaviour in each possible room configuration.
|
||||
|
||||
Also, ensure to have a look on [the documentation from Element Web](https://github.com/vector-im/element-web/blob/develop/docs/jitsi.md)
|
||||
|
||||
The official documentation about how to integrate the Jitsi SDK in an Android app is available here: https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk.
|
||||
|
||||
# Native Jitsi SDK
|
||||
## Native Jitsi SDK
|
||||
|
||||
The Jitsi SDK is built by ourselves with the flag LIBRE_BUILD, to be able to be integrated on the F-Droid version of Element Android.
|
||||
|
||||
The generated maven repository is then host in the project https://github.com/vector-im/jitsi_libre_maven
|
||||
|
||||
## How to build the Jitsi Meet SDK
|
||||
### How to build the Jitsi Meet SDK
|
||||
|
||||
### Jitsi version
|
||||
#### Jitsi version
|
||||
|
||||
Update the script `./tools/jitsi/build_jisti_libs.sh` with the tag of the project `https://github.com/jitsi/jitsi-meet`.
|
||||
|
||||
@ -22,7 +34,7 @@ Latest tag can be found from this page: https://github.com/jitsi/jitsi-meet-rele
|
||||
|
||||
Currently we are building the version with the tag `android-sdk-3.10.0`.
|
||||
|
||||
### Run the build script
|
||||
#### Run the build script
|
||||
|
||||
At the root of the Element Android, run the following script:
|
||||
|
||||
@ -32,7 +44,7 @@ At the root of the Element Android, run the following script:
|
||||
|
||||
It will build the Jitsi Meet Android library and put every generated files in the folder `/tmp/jitsi`
|
||||
|
||||
### Link with the new generated library
|
||||
#### Link with the new generated library
|
||||
|
||||
- Update the file `./build.gradle` to use the previously created local Maven repository. Currently we have this line:
|
||||
|
||||
@ -57,7 +69,7 @@ implementation('com.facebook.react:react-native-webrtc:1.92.1-jitsi-9093212@aar'
|
||||
- Perform a gradle sync and build the project
|
||||
- Perform test
|
||||
|
||||
### Sanity tests
|
||||
#### Sanity tests
|
||||
|
||||
In order to validate that the upgrade of the Jitsi and WebRTC dependency does not break anything, the following sanity tests have to be performed, using two devices:
|
||||
- Make 1-1 audio call (so using WebRTC)
|
||||
@ -65,7 +77,7 @@ In order to validate that the upgrade of the Jitsi and WebRTC dependency does no
|
||||
- Create and join a conference call with audio only (so using Jitsi library). Leave the conference. Join it again.
|
||||
- Create and join a conference call with audio and video (so using Jitsi library) Leave the conference. Join it again.
|
||||
|
||||
### Export the build library
|
||||
#### Export the build library
|
||||
|
||||
If all the tests are passed, you can export the generated Jitsi library to our Maven repository.
|
||||
|
||||
@ -81,4 +93,4 @@ url "https://github.com/vector-im/jitsi_libre_maven/raw/master/android-sdk-3.10.
|
||||
|
||||
- Build the project and perform the sanity tests again.
|
||||
|
||||
- Update the file `/CHANGES.md` to notify about the library upgrade, and create a regular PR for project Element Android.
|
||||
- Update the file `/CHANGES.md` to notify about the library upgrade, and create a regular PR for project Element Android.
|
||||
|
@ -1,37 +1,42 @@
|
||||
This document aims to describe how Element android displays notifications to the end user. It also clarifies notifications and background settings in the app.
|
||||
|
||||
# Table of Contents
|
||||
1. [Prerequisites Knowledge](#prerequisites-knowledge)
|
||||
* [How does a matrix client get a message from a homeserver?](#how-does-a-matrix-client-get-a-message-from-a-homeserver)
|
||||
* [How does a mobile app receives push notification?](#how-does-a-mobile-app-receives-push-notification)
|
||||
* [Push VS Notification](#push-vs-notification)
|
||||
* [Push in the matrix federated world](#push-in-the-matrix-federated-world)
|
||||
* [How does the homeserver know when to notify a client?](#how-does-the-homeserver-know-when-to-notify-a-client)
|
||||
* [Push vs privacy, and mitigation](#push-vs-privacy-and-mitigation)
|
||||
* [Background processing limitations](#background-processing-limitations)
|
||||
2. [Element Notification implementations](#element-notification-implementations)
|
||||
* [Requirements](#requirements)
|
||||
* [Foreground sync mode (Gplay & F-Droid)](#foreground-sync-mode-gplay-f-droid)
|
||||
* [Push (FCM) received in background](#push-fcm-received-in-background)
|
||||
* [FCM Fallback mode](#fcm-fallback-mode)
|
||||
* [F-Droid background Mode](#f-droid-background-mode)
|
||||
3. [Application Settings](#application-settings)
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Prerequisites Knowledge](#prerequisites-knowledge)
|
||||
* [How does a matrix client get a message from a homeserver?](#how-does-a-matrix-client-get-a-message-from-a-homeserver?)
|
||||
* [How does a mobile app receives push notification](#how-does-a-mobile-app-receives-push-notification)
|
||||
* [Push VS Notification](#push-vs-notification)
|
||||
* [Push in the matrix federated world](#push-in-the-matrix-federated-world)
|
||||
* [How does the homeserver know when to notify a client?](#how-does-the-homeserver-know-when-to-notify-a-client?)
|
||||
* [Push vs privacy, and mitigation](#push-vs-privacy-and-mitigation)
|
||||
* [Background processing limitations](#background-processing-limitations)
|
||||
* [Element Notification implementations](#element-notification-implementations)
|
||||
* [Requirements](#requirements)
|
||||
* [Foreground sync mode (Gplay and F-Droid)](#foreground-sync-mode-gplay-and-f-droid)
|
||||
* [Push (FCM) received in background](#push-fcm-received-in-background)
|
||||
* [FCM Fallback mode](#fcm-fallback-mode)
|
||||
* [F-Droid background Mode](#f-droid-background-mode)
|
||||
* [Application Settings](#application-settings)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
|
||||
First let's start with some prerequisite knowledge
|
||||
|
||||
# Prerequisites Knowledge
|
||||
## Prerequisites Knowledge
|
||||
|
||||
## How does a matrix client get a message from a homeserver?
|
||||
### How does a matrix client get a message from a homeserver?
|
||||
|
||||
In order to get messages from a homeserver, a matrix client need to perform a ``sync`` operation.
|
||||
|
||||
`To read events, the intended flow of operation is for clients to first call the /sync API without a since parameter. This returns the most recent message events for each room, as well as the state of the room at the start of the returned timeline. `
|
||||
|
||||
The client need to call the `sync`API periodically in order to get incremental updates of the server state (new messages).
|
||||
The client need to call the `sync` API periodically in order to get incremental updates of the server state (new messages).
|
||||
This mechanism is known as **HTTP long Polling**.
|
||||
|
||||
Using the **HTTP Long Polling** mechanism a client polls a server requesting new information.
|
||||
Using the **HTTP Long Polling** mechanism a client polls a server requesting new information.
|
||||
The server *holds the request open until new data is available*.
|
||||
Once available, the server responds and sends the new information.
|
||||
When the client receives the new information, it immediately sends another request, and the operation is repeated.
|
||||
@ -52,7 +57,7 @@ By default, this is 0, so the server will return immediately even if the respons
|
||||
|
||||
When the Element Android app is open (i.e in foreground state), the default timeout is 30 seconds, and delay is 0.
|
||||
|
||||
## How does a mobile app receives push notification
|
||||
### How does a mobile app receives push notification
|
||||
|
||||
Push notification is used as a way to wake up a mobile application when some important information is available and should be processed.
|
||||
|
||||
@ -66,22 +71,22 @@ FCM will only work on android devices that have Google plays services installed
|
||||
(In simple terms, Google Play Services is a background service that runs on Android, which in turn helps in integrating Google’s advanced functionalities to other applications)
|
||||
|
||||
De-Googlified devices need to rely on something else in order to stay up to date with a server.
|
||||
There some cases when devices with google services cannot use FCM (network infrastructure limitations -firewalls- ,
|
||||
privacy and or independency requirement, source code licence)
|
||||
There some cases when devices with google services cannot use FCM (network infrastructure limitations -firewalls-,
|
||||
privacy and or independence requirement, source code licence)
|
||||
|
||||
## Push VS Notification
|
||||
### Push VS Notification
|
||||
|
||||
This need some disambiguation, because it is the source of common confusion:
|
||||
|
||||
|
||||
*The fact that you see a notification on your screen does not mean that you have successfully configured your PUSH plateform.*
|
||||
*The fact that you see a notification on your screen does not mean that you have successfully configured your PUSH platform.*
|
||||
|
||||
Technically there is a difference between a push and a notification. A notification is what you see on screen and/or in the notification Menu/Drawer (in the top bar of the phone).
|
||||
|
||||
Notifications are not always triggered by a push (One can display a notification locally triggered by an alarm)
|
||||
|
||||
|
||||
## Push in the matrix federated world
|
||||
### Push in the matrix federated world
|
||||
|
||||
In order to send a push to a mobile, App developers need to have a server that will use the FCM APIs, and these APIs requires authentication!
|
||||
This server is called a **Push Gateway** in the matrix world
|
||||
@ -118,11 +123,11 @@ Client/Server API + | | | | |
|
||||
```
|
||||
|
||||
Recommended reading:
|
||||
* https://thomask.sdf.org/blog/2016/12/11/riots-magical-push-notifications-in-ios.html
|
||||
* https://thomask.sdf.org/blog/2016/12/11/riots-magical-push-notifications-in-ios.html
|
||||
* https://matrix.org/docs/spec/client_server/r0.4.0.html#id128
|
||||
|
||||
|
||||
## How does the homeserver know when to notify a client?
|
||||
### How does the homeserver know when to notify a client?
|
||||
|
||||
This is defined by [**push rules**](https://matrix.org/docs/spec/client_server/r0.4.0.html#push-rules-).
|
||||
|
||||
@ -140,14 +145,14 @@ Of course, content patterns matching cannot be used for encrypted messages serve
|
||||
|
||||
That is why clients are able to **process the push rules client side** to decide what kind of notification should be presented for a given event.
|
||||
|
||||
## Push vs privacy, and mitigation
|
||||
### Push vs privacy, and mitigation
|
||||
|
||||
As seen previously, App developers don't directly send a push to the end user's device, they use a Push Provider as intermediary. So technically this intermediary is able to read the content of what is sent.
|
||||
|
||||
App developers usually mitigate this by sending a `silent notification`, that is a notification with no identifiable data, or with an encrypted payload. When the push is received the app can then synchronise to it's server in order to generate a local notification.
|
||||
|
||||
|
||||
## Background processing limitations
|
||||
### Background processing limitations
|
||||
|
||||
A mobile applications process live in a managed word, meaning that its process can be limited (e.g no network access), stopped or killed at almost anytime by the Operating System.
|
||||
|
||||
@ -167,15 +172,15 @@ The documentation on this subject is vague, and as per our experiments not alway
|
||||
|
||||
It is getting more and more complex to have reliable notifications when FCM is not used.
|
||||
|
||||
# Element Notification implementations
|
||||
## Element Notification implementations
|
||||
|
||||
## Requirements
|
||||
### Requirements
|
||||
|
||||
Element Android must work with and without FCM.
|
||||
* The Element android app published on F-Droid do not rely on FCM (all related dependencies are not present)
|
||||
* The Element android app published on google play rely on FCM, with a fallback mode when FCM registration has failed (e.g outdated or missing Google Play Services)
|
||||
|
||||
## Foreground sync mode (Gplay & F-Droid)
|
||||
### Foreground sync mode (Gplay and F-Droid)
|
||||
|
||||
When in foreground, Element performs sync continuously with a timeout value set to 10 seconds (see HttpPooling).
|
||||
|
||||
@ -183,9 +188,9 @@ As this mode does not need to live beyond the scope of the application, and as p
|
||||
|
||||
This mode is turned on when the app enters foreground, and off when enters background.
|
||||
|
||||
In background, and depending on wether push is available or not, Element will use different methods to perform the syncs (Workers / Alarms / Service)
|
||||
In background, and depending on whether push is available or not, Element will use different methods to perform the syncs (Workers / Alarms / Service)
|
||||
|
||||
## Push (FCM) received in background
|
||||
### Push (FCM) received in background
|
||||
|
||||
In order to enable Push, Element must first get a push token from the firebase SDK, then register a pusher with this token on the homeserver.
|
||||
|
||||
@ -225,10 +230,10 @@ Upon reception of the FCM push, Element will perform a sync call to the homeserv
|
||||
|
||||
Element implements several strategies in these cases (TODO document)
|
||||
|
||||
## FCM Fallback mode
|
||||
### FCM Fallback mode
|
||||
|
||||
It is possible that Element is not able to get a FCM push token.
|
||||
Common errors (amoung several others) that can cause that:
|
||||
Common errors (among several others) that can cause that:
|
||||
* Google Play Services is outdated
|
||||
* Google Play Service fails in someways with FCM servers (infamous `SERVICE_NOT_AVAILABLE`)
|
||||
|
||||
@ -246,7 +251,7 @@ Usually in this mode, what happen is when you take back your phone in your hand,
|
||||
|
||||
The fallback mode is supposed to be a temporary state waiting for the user to fix issues for FCM, or for App Developers that has done a fork to correctly configure their FCM settings.
|
||||
|
||||
## F-Droid background Mode
|
||||
### F-Droid background Mode
|
||||
|
||||
The F-Droid Element flavor has no dependencies to FCM, therefore cannot relies on Push.
|
||||
|
||||
@ -256,7 +261,7 @@ Only solution left is to use `AlarmManager`, that offers new API to allow launch
|
||||
|
||||
Notice that these alarms, due to their potential impact on battery life, can still be restricted by the system. Documentation says that they will not be triggered more than every minutes under normal system operation, and when in low power mode about every 15 mn.
|
||||
|
||||
These restrictions can be relaxed by requirering the app to be white listed from battery optimization.
|
||||
These restrictions can be relaxed by requiring the app to be white listed from battery optimization.
|
||||
|
||||
F-Droid version will schedule alarms that will then trigger a Broadcast Receiver, that in turn will launch a Service (in the classic android way), and the reschedule an alarm for next time.
|
||||
|
||||
@ -266,9 +271,7 @@ That is why on Element F-Droid, the broadcast receiver will acquire a temporary
|
||||
|
||||
Note that foreground services require to put a notification informing the user that the app is doing something even if not launched).
|
||||
|
||||
|
||||
|
||||
# Application Settings
|
||||
## Application Settings
|
||||
|
||||
**Notifications > Enable notifications for this account**
|
||||
|
||||
|
@ -1,5 +1,43 @@
|
||||
# Pull requests
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Who should read this document?](#who-should-read-this-document?)
|
||||
* [Submitting PR](#submitting-pr)
|
||||
* [Who can submit pull requests?](#who-can-submit-pull-requests?)
|
||||
* [Humans](#humans)
|
||||
* [Draft PR?](#draft-pr?)
|
||||
* [Base branch](#base-branch)
|
||||
* [PR Review Assignment](#pr-review-assignment)
|
||||
* [PR review time](#pr-review-time)
|
||||
* [Re-request PR review](#re-request-pr-review)
|
||||
* [When create split PR?](#when-create-split-pr?)
|
||||
* [Avoid fixing other unrelated issue in a big PR](#avoid-fixing-other-unrelated-issue-in-a-big-pr)
|
||||
* [Bots](#bots)
|
||||
* [Dependabot](#dependabot)
|
||||
* [Gradle wrapper](#gradle-wrapper)
|
||||
* [Sync analytics plan](#sync-analytics-plan)
|
||||
* [Reviewing PR](#reviewing-pr)
|
||||
* [Who can review pull requests?](#who-can-review-pull-requests?)
|
||||
* [What to have in mind when reviewing a PR](#what-to-have-in-mind-when-reviewing-a-pr)
|
||||
* [Rules](#rules)
|
||||
* [Check the form](#check-the-form)
|
||||
* [PR title](#pr-title)
|
||||
* [PR description](#pr-description)
|
||||
* [File change](#file-change)
|
||||
* [Check the commit](#check-the-commit)
|
||||
* [Check the substance](#check-the-substance)
|
||||
* [Make a dedicated meeting to review the PR](#make-a-dedicated-meeting-to-review-the-pr)
|
||||
* [What happen to the issue(s)?](#what-happen-to-the-issues?)
|
||||
* [Merge conflict](#merge-conflict)
|
||||
* [When and who can merge PR](#when-and-who-can-merge-pr)
|
||||
* [Merge type](#merge-type)
|
||||
* [Resolve conversation](#resolve-conversation)
|
||||
* [Responsibility](#responsibility)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Introduction
|
||||
|
||||
This document gives some clue about how to efficiently manage Pull Requests (PR). This document is a first draft and may be improved later.
|
||||
|
@ -2,6 +2,27 @@
|
||||
|
||||
This document describes the flow of signin to a homeserver, and also the flow when user want to reset his password. Examples come from the `matrix.org` homeserver.
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Sign in flows](#sign-in-flows)
|
||||
* [Get the flow](#get-the-flow)
|
||||
* [Login with username](#login-with-username)
|
||||
* [Incorrect password](#incorrect-password)
|
||||
* [Correct password:](#correct-password:)
|
||||
* [Login with email](#login-with-email)
|
||||
* [Unknown email](#unknown-email)
|
||||
* [Known email, wrong password](#known-email-wrong-password)
|
||||
* [Known email, correct password](#known-email-correct-password)
|
||||
* [Login with Msisdn](#login-with-msisdn)
|
||||
* [Login with SSO](#login-with-sso)
|
||||
* [Reset password](#reset-password)
|
||||
* [Send email](#send-email)
|
||||
* [When the email is not known](#when-the-email-is-not-known)
|
||||
* [When the email is known](#when-the-email-is-known)
|
||||
* [User clicks on the link](#user-clicks-on-the-link)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Sign in flows
|
||||
|
||||
### Get the flow
|
||||
@ -322,4 +343,4 @@ curl -X POST --data $'{"auth":{"type":"m.login.email.identity","threepid_creds":
|
||||
{}
|
||||
```
|
||||
|
||||
The password has been changed, and all the existing token are invalidated. User can now login with the new password.
|
||||
The password has been changed, and all the existing token are invalidated. User can now login with the new password.
|
||||
|
@ -4,6 +4,20 @@ This document describes the flow of registration to a homeserver. Examples come
|
||||
|
||||
*Ref*: https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Sign up flows](#sign-up-flows)
|
||||
* [First step](#first-step)
|
||||
* [Step 1: entering user name and password](#step-1:-entering-user-name-and-password)
|
||||
* [If username already exists](#if-username-already-exists)
|
||||
* [Step 2: entering email](#step-2:-entering-email)
|
||||
* [Step 2 bis: user enters an email](#step-2-bis:-user-enters-an-email)
|
||||
* [Step 3: Accepting T&C](#step-3:-accepting-t&c)
|
||||
* [Step 4: Captcha](#step-4:-captcha)
|
||||
* [Step 5: MSISDN](#step-5:-msisdn)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Sign up flows
|
||||
|
||||
### First step
|
||||
|
@ -10,6 +10,20 @@ Currently the test are covering a small set of application flows:
|
||||
- Self verification via emoji
|
||||
- Self verification via passphrase
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Prerequisites:](#prerequisites:)
|
||||
* [Run the tests](#run-the-tests)
|
||||
* [From the source code](#from-the-source-code)
|
||||
* [From command line](#from-command-line)
|
||||
* [Recipes](#recipes)
|
||||
* [Wait for initial sync](#wait-for-initial-sync)
|
||||
* [Accessing current activity](#accessing-current-activity)
|
||||
* [Interact with other session](#interact-with-other-session)
|
||||
* [Contributing to the UiAllScreensSanityTest](#contributing-to-the-uiallscreenssanitytest)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Prerequisites:
|
||||
|
||||
Out of the box, the tests use one of the homeservers (located at http://localhost:8080) of the "Demo Federation of Homeservers" (https://github.com/matrix-org/synapse#running-a-demo-federation-of-synapses).
|
||||
|
2
fastlane/metadata/android/cs-CZ/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Hlavní změny v této verzi: Zlepšení správy ignorovaných uživatelů. Opravy různých chyb a vylepšení stability.
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/en-US/changelogs/40104180.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40104180.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Main changes in this version: Various bug fixes and stability improvements.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/es-ES/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/es-ES/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Cambios principales en esta versión: Mejoras en la administración de usuarios ignorados. Varias correciones de bugs y mejoras en la estabilidad.
|
||||
Registro de cambios: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/et/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Põhilised muutused selles versioonis: eiratud kasutajate parem haldus ning erinevate vigade parandused ja stabiilsust edendavad kohendused.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/fa/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/fa/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
تغییرات عمده در این نگارش: مدیریت بهبودیافتهٔ کاربران چشمپوشیده. رفع اشکالهای مختلف و بهبودهای پایداری.
|
||||
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/fr-FR/changelogs/40104100.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40104100.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Principaux changements pour cette version : Défilement dans les messages vocaux. Plusieurs corrections de bogues et d’améliorations de stabilité.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/fr-FR/changelogs/40104110.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40104110.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Principaux changements pour cette version : Plusieurs corrections de bogues et d’améliorations de stabilité.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/fr-FR/changelogs/40104120.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40104120.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Principaux changements pour cette version : Les utilisateurs peuvent apparaître hors-ligne. Ajout d’un lecteur pour les pièces jointes audio
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/fr-FR/changelogs/40104130.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40104130.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Principaux changements pour cette version : Les utilisateurs peuvent apparaître hors-ligne. Ajout d’un lecteur pour les pièces jointes audio
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/fr-FR/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Principaux changements pour cette version : Amélioration de la gestion des utilisateurs ignorés. Plusieurs corrections de bogues et d’améliorations de stabilité.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/id/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Perubahan utama dalam versi ini: Tingkatkan pengelolaan pengguna yang diabaikan. Beberapa perbaikan kutu dan stabilitas.
|
||||
Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/it-IT/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Modifiche principali in questa versione: migliorata la gestione degli utenti ignorati. Varie correzioni e miglioramenti della stabilità.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/lo-LA/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/lo-LA/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
ການປ່ຽນແປງຫຼັກໃນສະບັບນີ້: ປັບປຸງການບໍລິຫານການລະເວັ້ນຜູ້ໃຊ້. ປັບປຸງບັກ ແລະຄວາມສະຖຽນ.
|
||||
ບັນທຶກການປ່ຽນແປງສະບັບເຕັມ: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/pl-PL/changelogs/40101120.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40101120.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: aktualizacja motywu i stylu oraz naprawa awarii po rozmowie wideo
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.1.12
|
2
fastlane/metadata/android/pl-PL/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40101130.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: głównie aktualizacja stabilności i poprawki błędów.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/pl-PL/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40101140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: naprawienie problemu z zaszyfrowanymi wiadomościami.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/pl-PL/changelogs/40101150.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40101150.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: implementacja wiadomości głosowych w ustawieniach laboratorium.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.1.15
|
2
fastlane/metadata/android/pl-PL/changelogs/40101160.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40101160.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Naprawiono błąd podczas wysyłania zaszyfrowanej wiadomości, jeśli ktoś w pokoju się wyloguje.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
2
fastlane/metadata/android/pl-PL/changelogs/40102000.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40102000.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Wiadomość głosowa jest domyślnie włączona.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.2.0
|
2
fastlane/metadata/android/pl-PL/changelogs/40102010.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40102010.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Wiele ulepszeń w VoIP i Przestrzeniach (nadal w wersji beta).
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.2.1
|
2
fastlane/metadata/android/pl-PL/changelogs/40103000.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40103000.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Organizuj swoje pokoje za pomocą Przestrzeni!
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.0
|
2
fastlane/metadata/android/pl-PL/changelogs/40103010.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40103010.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Organizuj swoje pokoje za pomocą Przestrzeni! Wersja 1.3.1 naprawia awarię, która może wystąpić w wersji 1.3.0.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.1
|
2
fastlane/metadata/android/pl-PL/changelogs/40103020.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40103020.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Dodano obsługę Android Auto. Wiele poprawek błędów!
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.2
|
2
fastlane/metadata/android/pl-PL/changelogs/40103030.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40103030.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Uwidocznij politykę(-i) serwera tożsamości w ustawieniach. Tymczasowo usunięto obsługę Androida Auto.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.3
|
2
fastlane/metadata/android/pl-PL/changelogs/40103040.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40103040.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Dodanie obsługi obecności, dla pokoju wiadomości bezpośrednich (uwaga: obecność jest wyłączona na matrix.org). Dodano ponownie obsługę Androida Auto.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.4
|
2
fastlane/metadata/android/pl-PL/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Główne zmiany w tej wersji: Poprawa zarządzania ignorowanymi użytkownikami. Różne poprawki błędów i ulepszenia stabilności.
|
||||
Pełna lista zmian: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/pt-BR/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Principais mudanças nesta versão: Melhorar gerenciamento de usuárias(os) ignoradas(os). Vários consertos de bugs e melhorias de estabilidade.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/sk/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Hlavné zmeny v tejto verzii: Zlepšenie správy ignorovaných používateľov. Rôzne opravy chýb a vylepšenia stability.
|
||||
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/sv-SE/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/sv-SE/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Huvudsakliga ändringar i den här versionen: Förbättra hantering av ignorerade användare. Diverse buggfixar och stabilitetsförbättringar.
|
||||
Full ändringslogg: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/ta-IN/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/ta-IN/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
இந்த பதிப்பில் உள்ள முதன்மை மாற்றங்கள்: தவிர்க்கப்பட்ட பயனர்களின் மேலாண்மை மேம்படுத்தப்பட்டுள்ளது. வெவ்வேறு வழுக்களைச் சரிசெய்தல் மற்றும் நிலைப்புத்தன்மையை மேம்படுத்தல்.
|
||||
முழு மாற்ற அறிக்கை: https://github.com/vector-im/element-android/releases
|
42
fastlane/metadata/android/ta-IN/full_description.txt
Normal file
42
fastlane/metadata/android/ta-IN/full_description.txt
Normal file
@ -0,0 +1,42 @@
|
||||
Element is both a secure messenger and a productivity team collaboration app that is ideal for group chats while remote working. This chat app uses end-to-end encryption to provide powerful video conferencing, file sharing and voice calls.
|
||||
|
||||
<b>Element இன் தனிச்சிறப்புகளுள் சில:</b>
|
||||
- மேம்பட்ட இயங்கலை தொடர்பு கருவிகள்
|
||||
- தொலைநிலையில் உள்ள ஊழியர்களுக்கும், பாதுகாப்பான நிறும கருத்து பரிமாற்றங்களை அனுமதிப்பதற்காக, முழுவதுமாக மறைகுறியாக்கப்பட்ட செய்திகள்
|
||||
- MATRIX திறந்த மூல கட்டமைப்பை அடிப்படையாக கொண்டு செயல்படும் அதிகாரப்பரவலாக்கப்பட்ட அரட்டை
|
||||
- செயல்திட்டங்களை மேலாண்மை செய்யும் போது, மறைகுறியாக்கப்பட்ட தரவுடன் கூடிய பாதுகாப்பான கோப்பு பகிரல்
|
||||
- IP மூலம் குரல் (VoIP) மற்றும் திரை பகிரல் உடன் கூடிய குரல் அரட்டைகள்
|
||||
- உங்கள் மனம் கவர்ந்த இயங்கலை உடனிணைவு கருவிகள், செயல்திட்ட மேலாண்மை கருவிகள், VoIP சேவைகள் மற்றும் இதர குழு தூதுரை செயலிகள் உடன் கூடிய எளிமையான ஒருமைப்பாடு
|
||||
|
||||
Element is completely different from other messaging and collaboration apps. It operates on Matrix, an open network for secure messaging and decentralized communication. It allows self-hosting to give users maximum ownership and control of their data and messages.
|
||||
|
||||
<b>தனியுரிமை மற்றும் மறைகுறியாக்கப்பட்ட செய்தி அனுப்பல்</b>
|
||||
தேவையில்லாத விளம்பரங்கள், தரவு சுரண்டல் மற்றும் தகவல் கட்டுப்பாடு போன்றவற்றில் இருந்து Element உங்களை பாதுகாக்கிறது. மேலும், இது முனைக்கு-முனை மறைகுறியாக்கம் மற்றும் குறுக்கு-ஒப்பமிடப்பட்ட சாதன சரிபார்ப்பு ஆகியவற்றின் மூலம் உங்கள் எல்லா தரவுகள், ஒன்றுக்கொன்றான காணொளி மற்றும் குரல் அழைப்புகளை பாதுகாக்கிறது.
|
||||
|
||||
Element gives you control over your privacy while allowing you to communicate securely with யாரோனும் ஒருவருடன் on the Matrix network, or other business collaboration tools by integrating with apps such as Slack.
|
||||
|
||||
<b>Element can be self-hosted</b>
|
||||
To allow more control of your sensitive data and conversations, Element can be self-hosted or you can choose any Matrix-based host - the standard for open source, decentralized communication. Element gives you privacy, security compliance and integration flexibility.
|
||||
|
||||
<b>உங்கள் தரவைச் சொந்தமாக்கிக் கொள்ளுங்கள்</b>
|
||||
தரவுகள் மற்றும் செய்திகளை எங்கு சேமித்து வைக்க வேண்டும் என்பதை நீங்கள் முடிவு செய்கிறீர்கள். இதன்மூலம், தரவு சுரண்டல் மற்றும் மூன்றாம் தரப்பினர் அனுகல் ஆகிய இடர்களை தவிர்க்கலாம்.
|
||||
|
||||
Element வெவ்வேறு வகையில் கட்டுப்பாட்டை உங்களிடம் அளிக்கிறது:
|
||||
1. Get a free account on the matrix.org public server hosted by the Matrix developers, or choose from thousands of public servers hosted by volunteers
|
||||
2. Self-host your account by running a server on your own IT infrastructure
|
||||
3. Sign up for an account on a custom server by simply subscribing to the Element Matrix Services hosting platform
|
||||
|
||||
<b>திறந்த செய்தி அனுப்பல் மற்றும் ஒருமைப்பாடு</b>
|
||||
You can chat with anyone on the Matrix network, whether they’re using Element, another Matrix app or even if they are using a different messaging app.
|
||||
|
||||
<b>மிகவும் பாதுகாப்பானது</b>
|
||||
உண்மையான முனைக்கு-முனை மறைகுறியாக்கம் (உரையாடலில் உள்ளவர்கள் மட்டுமே மறைகுறியாக்கத்தை நீக்கி செய்தியை காண இயலும்) மற்றும் குறுக்கு-ஒப்பமிடப்பட்ட சாதன சரிபார்ப்பு.
|
||||
|
||||
<b>முழுமையான தொடர்பு மற்றும் ஒருமைப்பாடு</b>
|
||||
செய்தி அனுப்பல், காணொளி மற்றும் குரல் அழைப்புகளை, கோப்பு பகிரல், திரை பகிரல் மற்றும் ஒருமைப்பாடுகள், இயலிகள் மற்றும் நிரல் பலகைகளின் மொத்த கொத்து. அறைகள், குழுக்களை உருவாக்கி, அவர்களுடன் உரையாடி, வேலையை எளிமையாக்கவும்.
|
||||
|
||||
<b>எங்கு விட்டு சென்றீர்களோ அதிலிருந்த துவங்கவும்</b>
|
||||
Stay in touch wherever you are with fully synchronised message history across all your devices and on the web at https://app.element.io
|
||||
|
||||
<b>திறந்த மூலம்</b>
|
||||
Element Android ஒரு திறந்த மூல செயல் திட்டமாகும். இது GitHub இல் தொகுத்து வழங்கப்பட்டுள்ளது. வழுக்கள் ஏதேனும் கண்டறிந்தால் மற்றும்/அல்லது இதன் வளர்ச்சிக்கு பங்களிக்க விரும்பினால், https://github.com/vector-im/element-android என்னும் தளத்திற்கு வருகை தரவும்.
|
1
fastlane/metadata/android/ta-IN/short_description.txt
Normal file
1
fastlane/metadata/android/ta-IN/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
மறைகுறியாக்கப்பட்ட செய்தி அனுப்பல், குழு அரட்டை மற்றும் காணொளி அழைப்புகள்
|
1
fastlane/metadata/android/ta-IN/title.txt
Normal file
1
fastlane/metadata/android/ta-IN/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Element - பாதுகாப்பான தூதுரை சேவை
|
2
fastlane/metadata/android/uk/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Основні зміни у цій версії: Удосконалено керування нехтуваними користувачами. Різні виправлення помилок та поліпшення стабільності.
|
||||
Вичерпний журнал змін: https://github.com/vector-im/element-android/releases
|
2
fastlane/metadata/android/zh-TW/changelogs/40104140.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40104140.txt
Normal file
@ -0,0 +1,2 @@
|
||||
此版本中的主要變動:改善被忽略使用者的管理。多個臭蟲修復與穩定性改善。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases
|
@ -55,5 +55,6 @@ dependencies {
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.recyclerview
|
||||
|
||||
implementation libs.google.material
|
||||
}
|
||||
api libs.androidx.viewpager2
|
||||
implementation libs.androidx.transition
|
||||
}
|
||||
|
@ -50,6 +50,5 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.lib.core.utils.flow
|
||||
|
||||
import android.os.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
@ -68,10 +69,10 @@ fun <T> Flow<T>.chunk(durationInMillis: Long): Flow<List<T>> {
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
|
||||
var windowStartTime = System.currentTimeMillis()
|
||||
var windowStartTime = SystemClock.elapsedRealtime()
|
||||
var emitted = false
|
||||
collect { value ->
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val currentTime = SystemClock.elapsedRealtime()
|
||||
val delta = currentTime - windowStartTime
|
||||
if (delta >= windowDuration) {
|
||||
windowStartTime += delta / windowDuration * windowDuration
|
||||
|
@ -52,6 +52,7 @@ dependencies {
|
||||
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.core
|
||||
implementation libs.androidx.recyclerview
|
||||
|
||||
implementation libs.airbnb.epoxy
|
||||
kapt libs.airbnb.epoxyProcessor
|
||||
@ -60,7 +61,6 @@ dependencies {
|
||||
// Span utils
|
||||
implementation 'me.gujun.android:span:1.7'
|
||||
|
||||
implementation libs.google.material
|
||||
|
||||
implementation libs.jetbrains.coroutinesCore
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
|
@ -18,11 +18,11 @@ package org.billcarsonfr.jsonviewer
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.view.ContextMenu
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.getSystemService
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyHolder
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
@ -77,8 +77,7 @@ internal abstract class ValueItem : EpoxyModelWithHolder<ValueItem.Holder>() {
|
||||
) {
|
||||
if (copyValue != null) {
|
||||
val menuItem = menu?.add(R.string.copy_value)
|
||||
val clipService =
|
||||
v?.context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
|
||||
val clipService = v?.context?.getSystemService<ClipboardManager>()
|
||||
menuItem?.setOnMenuItemClickListener {
|
||||
clipService?.setPrimaryClip(ClipData.newPlainText("", copyValue))
|
||||
true
|
||||
|
@ -38,9 +38,9 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.fragmentKtx
|
||||
api libs.androidx.activity
|
||||
implementation libs.androidx.exifinterface
|
||||
implementation libs.androidx.core
|
||||
|
||||
// Log
|
||||
implementation libs.jakewharton.timber
|
||||
|
@ -60,4 +60,4 @@ dependencies {
|
||||
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
|
||||
// dialpad dimen
|
||||
implementation 'im.dlg:android-dialer:1.2.5'
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,4 @@
|
||||
android:height="70dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
</ripple>
|
||||
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:angle="@integer/rtl_mirror_flip"
|
||||
android:endColor="#55DFD1FF"
|
||||
android:startColor="#55A5F2E0" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="@android:color/transparent"
|
||||
android:startColor="?android:colorBackground" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
@ -10,4 +10,4 @@
|
||||
<size
|
||||
android:width="24dp"
|
||||
android:height="24dp"/>
|
||||
</shape>
|
||||
</shape>
|
||||
|
@ -9,4 +9,4 @@
|
||||
<size
|
||||
android:width="24dp"
|
||||
android:height="24dp"/>
|
||||
</shape>
|
||||
</shape>
|
||||
|
@ -6,4 +6,4 @@
|
||||
android:drawable="@drawable/pin_code_dot_fill"/>
|
||||
<item
|
||||
android:drawable="@drawable/pin_code_dot_empty"/>
|
||||
</selector>
|
||||
</selector>
|
||||
|
@ -2,10 +2,42 @@
|
||||
<resources>
|
||||
|
||||
<style name="Widget.Vector.Button.Text.OnPrimary.LocationLive">
|
||||
<item name="android:background">?selectableItemBackground</item>
|
||||
<item name="android:foreground">?selectableItemBackground</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Vector.Button.Text.LocationLive">
|
||||
<item name="android:foreground">?selectableItemBackground</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Vector.Body.Medium</item>
|
||||
<item name="android:textColor">?colorError</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Vector.Body.BottomSheetDisplayName">
|
||||
<item name="android:textSize">16sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Vector.Body.BottomSheetRemainingTime">
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Vector.Body.BottomSheetLastUpdatedAt">
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:textColor">?vctr_content_tertiary</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Vector.Button.Text.BottomSheetStopSharing">
|
||||
<item name="android:foreground">?selectableItemBackground</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Vector.Body.Medium</item>
|
||||
<item name="android:textColor">?colorError</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -41,4 +41,4 @@
|
||||
<item name="android:textColor">?vctr_content_primary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="TimelineContentStubBaseParams">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
@ -33,5 +33,8 @@
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="TimelineFixedSizeCaptionStyle" parent="@style/Widget.Vector.TextView.Caption">
|
||||
<item name="android:textSize" tools:ignore="SpUsage">12dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -31,9 +31,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation libs.androidx.appCompat
|
||||
|
||||
implementation libs.jetbrains.coroutinesCore
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
@ -41,7 +39,4 @@ dependencies {
|
||||
|
||||
// Paging
|
||||
implementation libs.androidx.pagingRuntimeKtx
|
||||
|
||||
// Logging
|
||||
implementation libs.jakewharton.timber
|
||||
}
|
||||
|
@ -45,6 +45,13 @@ import org.matrix.android.sdk.api.util.toOptional
|
||||
|
||||
class FlowSession(private val session: Session) {
|
||||
|
||||
fun liveRoomSummary(roomId: String): Flow<Optional<RoomSummary>> {
|
||||
return session.roomService().getRoomSummaryLive(roomId).asFlow()
|
||||
.startWith(session.coroutineDispatchers.io) {
|
||||
session.roomService().getRoomSummary(roomId).toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams, sortOrder: RoomSortOrder = RoomSortOrder.NONE): Flow<List<RoomSummary>> {
|
||||
return session.roomService().getRoomSummariesLive(queryParams, sortOrder).asFlow()
|
||||
.startWith(session.coroutineDispatchers.io) {
|
||||
|
@ -56,7 +56,7 @@ android {
|
||||
// that the app's state is completely cleared between tests.
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.4.16\""
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.4.18\""
|
||||
|
||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
||||
@ -136,7 +136,6 @@ dependencies {
|
||||
implementation libs.jetbrains.coroutinesCore
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.core
|
||||
|
||||
// Lifecycle
|
||||
@ -155,12 +154,11 @@ dependencies {
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
|
||||
implementation 'com.squareup.okhttp3:okhttp'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor'
|
||||
implementation 'com.squareup.okhttp3:okhttp-urlconnection'
|
||||
|
||||
implementation libs.squareup.moshi
|
||||
kapt libs.squareup.moshiKotlin
|
||||
|
||||
implementation libs.markwon.core
|
||||
api "com.atlassian.commonmark:commonmark:0.13.0"
|
||||
|
||||
// Image
|
||||
implementation libs.androidx.exifinterface
|
||||
@ -176,10 +174,6 @@ dependencies {
|
||||
// Work
|
||||
implementation libs.androidx.work
|
||||
|
||||
// FP
|
||||
implementation libs.arrow.core
|
||||
implementation libs.arrow.instances
|
||||
|
||||
// olm lib is now hosted in MavenCentral
|
||||
implementation 'org.matrix.android:olm-sdk:3.2.11'
|
||||
|
||||
@ -198,11 +192,9 @@ dependencies {
|
||||
implementation libs.apache.commonsImaging
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.48'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.49'
|
||||
|
||||
testImplementation libs.tests.junit
|
||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||
testImplementation libs.mockk.mockk
|
||||
testImplementation libs.tests.kluent
|
||||
|
@ -1,3 +1,7 @@
|
||||
# Package org.matrix.android.sdk.userstories
|
||||
|
||||
This package contains some user stories (**Us** prefix) of the SDK usage. You will find example of what it is possible to do with the SDK and the API which can be used to do it.
|
||||
|
||||
# Package org.matrix.android.sdk.api
|
||||
|
||||
This is the root package of the API exposed by this SDK.
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk
|
||||
|
||||
import junit.framework.TestCase.fail
|
||||
|
||||
/**
|
||||
* Will fail the test if invoking [block] is not throwing a Throwable.
|
||||
*
|
||||
* @param message the failure message, if the block does not throw any Throwable
|
||||
* @param failureBlock a Lambda to be able to do extra check on the thrown Throwable
|
||||
* @param block the block to test
|
||||
*/
|
||||
internal suspend fun mustFail(
|
||||
message: String = "must fail",
|
||||
failureBlock: ((Throwable) -> Unit)? = null,
|
||||
block: suspend () -> Unit,
|
||||
) {
|
||||
val isSuccess = try {
|
||||
block.invoke()
|
||||
true
|
||||
} catch (throwable: Throwable) {
|
||||
failureBlock?.invoke(throwable)
|
||||
false
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
fail(message)
|
||||
}
|
||||
}
|
@ -24,8 +24,8 @@ import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
|
||||
@ -34,32 +34,22 @@ import org.matrix.android.sdk.common.TestConstants
|
||||
@LargeTest
|
||||
class AccountCreationTest : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
@Test
|
||||
fun createAccountTest() {
|
||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||
|
||||
commonTestHelper.signOutAndClose(session)
|
||||
fun createAccountTest() = runSessionTest(context()) { commonTestHelper ->
|
||||
commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
fun createAccountAndLoginAgainTest() {
|
||||
fun createAccountAndLoginAgainTest() = runSessionTest(context()) { commonTestHelper ->
|
||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||
|
||||
// Log again to the same account
|
||||
val session2 = commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
|
||||
|
||||
commonTestHelper.signOutAndClose(session)
|
||||
commonTestHelper.signOutAndClose(session2)
|
||||
commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simpleE2eTest() {
|
||||
val res = cryptoTestHelper.doE2ETestWithAliceInARoom()
|
||||
|
||||
res.cleanUp(commonTestHelper)
|
||||
fun simpleE2eTest() = runCryptoTest(context()) { cryptoTestHelper, _ ->
|
||||
cryptoTestHelper.doE2ETestWithAliceInARoom()
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
|
||||
@ -34,14 +34,12 @@ import org.matrix.android.sdk.common.TestConstants
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
class ChangePasswordTest : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
|
||||
companion object {
|
||||
private const val NEW_PASSWORD = "this is a new password"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun changePasswordTest() {
|
||||
fun changePasswordTest() = runSessionTest(context()) { commonTestHelper ->
|
||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
||||
|
||||
// Change password
|
||||
@ -54,9 +52,6 @@ class ChangePasswordTest : InstrumentedTest {
|
||||
throwable.isInvalidPassword().shouldBeTrue()
|
||||
|
||||
// Try to login with the new password, should work
|
||||
val session2 = commonTestHelper.logIntoAccount(session.myUserId, NEW_PASSWORD, SessionTestParams(withInitialSync = false))
|
||||
|
||||
commonTestHelper.signOutAndClose(session)
|
||||
commonTestHelper.signOutAndClose(session2)
|
||||
commonTestHelper.logIntoAccount(session.myUserId, NEW_PASSWORD, SessionTestParams(withInitialSync = false))
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import kotlin.coroutines.Continuation
|
||||
@ -39,10 +39,8 @@ import kotlin.coroutines.resume
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class DeactivateAccountTest : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
|
||||
@Test
|
||||
fun deactivateAccountTest() {
|
||||
fun deactivateAccountTest() = runSessionTest(context(), false /* session will be deactivated */) { commonTestHelper ->
|
||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||
|
||||
// Deactivate the account
|
||||
|
@ -23,7 +23,7 @@ import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import timber.log.Timber
|
||||
@ -32,10 +32,8 @@ import timber.log.Timber
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class ApiInterceptorTest : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
|
||||
@Test
|
||||
fun apiInterceptorTest() {
|
||||
fun apiInterceptorTest() = runSessionTest(context()) { commonTestHelper ->
|
||||
val responses = mutableListOf<String>()
|
||||
|
||||
val listener = object : ApiInterceptorListener {
|
||||
|
@ -54,12 +54,39 @@ import java.util.concurrent.TimeUnit
|
||||
* This class exposes methods to be used in common cases
|
||||
* Registration, login, Sync, Sending messages...
|
||||
*/
|
||||
class CommonTestHelper(context: Context) {
|
||||
class CommonTestHelper private constructor(context: Context) {
|
||||
|
||||
companion object {
|
||||
internal fun runSessionTest(context: Context, autoSignoutOnClose: Boolean = true, block: (CommonTestHelper) -> Unit) {
|
||||
val testHelper = CommonTestHelper(context)
|
||||
return try {
|
||||
block(testHelper)
|
||||
} finally {
|
||||
if (autoSignoutOnClose) {
|
||||
testHelper.cleanUpOpenedSessions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun runCryptoTest(context: Context, autoSignoutOnClose: Boolean = true, block: (CryptoTestHelper, CommonTestHelper) -> Unit) {
|
||||
val testHelper = CommonTestHelper(context)
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
return try {
|
||||
block(cryptoTestHelper, testHelper)
|
||||
} finally {
|
||||
if (autoSignoutOnClose) {
|
||||
testHelper.cleanUpOpenedSessions()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val matrix: TestMatrix
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private var accountNumber = 0
|
||||
|
||||
private val trackedSessions = mutableListOf<Session>()
|
||||
|
||||
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
||||
|
||||
init {
|
||||
@ -84,6 +111,15 @@ class CommonTestHelper(context: Context) {
|
||||
return logIntoAccount(userId, TestConstants.PASSWORD, testParams)
|
||||
}
|
||||
|
||||
fun cleanUpOpenedSessions() {
|
||||
trackedSessions.forEach {
|
||||
runBlockingTest {
|
||||
it.signOutService().signOut(true)
|
||||
}
|
||||
}
|
||||
trackedSessions.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a homeserver configuration, with Http connection allowed for test
|
||||
*/
|
||||
@ -96,7 +132,7 @@ class CommonTestHelper(context: Context) {
|
||||
/**
|
||||
* This methods init the event stream and check for initial sync
|
||||
*
|
||||
* @param session the session to sync
|
||||
* @param session the session to sync
|
||||
*/
|
||||
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) {
|
||||
val lock = CountDownLatch(1)
|
||||
@ -119,7 +155,7 @@ class CommonTestHelper(context: Context) {
|
||||
/**
|
||||
* This methods clear the cache and waits for initialSync
|
||||
*
|
||||
* @param session the session to sync
|
||||
* @param session the session to sync
|
||||
*/
|
||||
fun clearCacheAndSync(session: Session, timeout: Long = TestConstants.timeOutMillis) {
|
||||
waitWithLatch(timeout) { latch ->
|
||||
@ -142,8 +178,8 @@ class CommonTestHelper(context: Context) {
|
||||
/**
|
||||
* Sends text messages in a room
|
||||
*
|
||||
* @param room the room where to send the messages
|
||||
* @param message the message to send
|
||||
* @param room the room where to send the messages
|
||||
* @param message the message to send
|
||||
* @param nbOfMessages the number of time the message will be sent
|
||||
*/
|
||||
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
|
||||
@ -207,8 +243,8 @@ class CommonTestHelper(context: Context) {
|
||||
|
||||
/**
|
||||
* Reply in a thread
|
||||
* @param room the room where to send the messages
|
||||
* @param message the message to send
|
||||
* @param room the room where to send the messages
|
||||
* @param message the message to send
|
||||
* @param numberOfMessages the number of time the message will be sent
|
||||
*/
|
||||
fun replyInThreadMessage(
|
||||
@ -232,8 +268,8 @@ class CommonTestHelper(context: Context) {
|
||||
* Creates a unique account
|
||||
*
|
||||
* @param userNamePrefix the user name prefix
|
||||
* @param password the password
|
||||
* @param testParams test params about the session
|
||||
* @param password the password
|
||||
* @param testParams test params about the session
|
||||
* @return the session associated with the newly created account
|
||||
*/
|
||||
private fun createAccount(userNamePrefix: String,
|
||||
@ -245,14 +281,16 @@ class CommonTestHelper(context: Context) {
|
||||
testParams
|
||||
)
|
||||
assertNotNull(session)
|
||||
return session
|
||||
return session.also {
|
||||
trackedSessions.add(session)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs into an existing account
|
||||
*
|
||||
* @param userId the userId to log in
|
||||
* @param password the password to log in
|
||||
* @param userId the userId to log in
|
||||
* @param password the password to log in
|
||||
* @param testParams test params about the session
|
||||
* @return the session associated with the existing account
|
||||
*/
|
||||
@ -261,14 +299,16 @@ class CommonTestHelper(context: Context) {
|
||||
testParams: SessionTestParams): Session {
|
||||
val session = logAccountAndSync(userId, password, testParams)
|
||||
assertNotNull(session)
|
||||
return session
|
||||
return session.also {
|
||||
trackedSessions.add(session)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an account and a dedicated session
|
||||
*
|
||||
* @param userName the account username
|
||||
* @param password the password
|
||||
* @param userName the account username
|
||||
* @param password the password
|
||||
* @param sessionTestParams parameters for the test
|
||||
*/
|
||||
private fun createAccountAndSync(userName: String,
|
||||
@ -297,7 +337,7 @@ class CommonTestHelper(context: Context) {
|
||||
val session = (registrationResult as RegistrationResult.Success).session
|
||||
session.open()
|
||||
if (sessionTestParams.withInitialSync) {
|
||||
syncSession(session, 60_000)
|
||||
syncSession(session, 120_000)
|
||||
}
|
||||
return session
|
||||
}
|
||||
@ -305,8 +345,8 @@ class CommonTestHelper(context: Context) {
|
||||
/**
|
||||
* Start an account login
|
||||
*
|
||||
* @param userName the account username
|
||||
* @param password the password
|
||||
* @param userName the account username
|
||||
* @param password the password
|
||||
* @param sessionTestParams session test params
|
||||
*/
|
||||
private fun logAccountAndSync(userName: String,
|
||||
@ -378,7 +418,10 @@ class CommonTestHelper(context: Context) {
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
fun await(latch: CountDownLatch, timeout: Long? = TestConstants.timeOutMillis) {
|
||||
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
|
||||
assertTrue(
|
||||
"Timed out after " + timeout + "ms waiting for something to happen. See stacktrace for cause.",
|
||||
latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
|
||||
@ -433,6 +476,7 @@ class CommonTestHelper(context: Context) {
|
||||
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
|
||||
|
||||
fun signOutAndClose(session: Session) {
|
||||
trackedSessions.remove(session)
|
||||
runBlockingTest(timeout = 60_000) {
|
||||
session.signOutService().signOut(true)
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.securestorage.EmptyKeySigner
|
||||
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
|
||||
import org.matrix.android.sdk.api.session.securestorage.KeyRef
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.awaitCallback
|
||||
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||
@ -66,7 +66,7 @@ import java.util.UUID
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
class CryptoTestHelper(val testHelper: CommonTestHelper) {
|
||||
|
||||
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
|
||||
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
|
||||
@ -361,19 +361,19 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
ssssService.storeSecret(
|
||||
MASTER_KEY_SSSS_NAME,
|
||||
session.cryptoService().crossSigningService().getCrossSigningPrivateKeys()!!.master!!,
|
||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
listOf(KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
)
|
||||
|
||||
ssssService.storeSecret(
|
||||
SELF_SIGNING_KEY_SSSS_NAME,
|
||||
session.cryptoService().crossSigningService().getCrossSigningPrivateKeys()!!.selfSigned!!,
|
||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
listOf(KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
)
|
||||
|
||||
ssssService.storeSecret(
|
||||
USER_SIGNING_KEY_SSSS_NAME,
|
||||
session.cryptoService().crossSigningService().getCrossSigningPrivateKeys()!!.user!!,
|
||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
listOf(KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
)
|
||||
|
||||
// set up megolm backup
|
||||
@ -390,7 +390,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
ssssService.storeSecret(
|
||||
KEYBACKUP_SECRET_SSSS_NAME,
|
||||
secret,
|
||||
listOf(SharedSecretStorageService.KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
listOf(KeyRef(keyInfo.keyId, keyInfo.keySpec))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ class RetryTestRule(val retryCount: Int = 3) : TestRule {
|
||||
for (i in 0 until retryCount) {
|
||||
try {
|
||||
base.evaluate()
|
||||
if (i > 0) {
|
||||
println("Retried test $i times")
|
||||
}
|
||||
return
|
||||
} catch (t: Throwable) {
|
||||
caughtThrowable = t
|
||||
|
@ -23,7 +23,7 @@ object TestConstants {
|
||||
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
|
||||
|
||||
// Time out to use when waiting for server response.
|
||||
private const val AWAIT_TIME_OUT_MILLIS = 60_000
|
||||
private const val AWAIT_TIME_OUT_MILLIS = 120_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
|
||||
|
@ -21,7 +21,8 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.impl.WorkManagerImpl
|
||||
import androidx.work.impl.utils.taskexecutor.WorkManagerTaskExecutor
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
@ -66,7 +67,12 @@ internal class TestMatrix(context: Context, matrixConfiguration: MatrixConfigura
|
||||
.setExecutor(Executors.newCachedThreadPool())
|
||||
.setWorkerFactory(matrixWorkerFactory)
|
||||
.build()
|
||||
WorkManager.initialize(appContext, configuration)
|
||||
val delegate = WorkManagerImpl(
|
||||
context,
|
||||
configuration,
|
||||
WorkManagerTaskExecutor(configuration.taskExecutor)
|
||||
)
|
||||
WorkManagerImpl.setDelegate(delegate)
|
||||
uiHandler.post {
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
|
||||
}
|
||||
|
@ -22,9 +22,11 @@ import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||
@ -37,6 +39,8 @@ private const val DUMMY_DEVICE_KEY = "DeviceKey"
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CryptoStoreTest : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
private val cryptoStoreHelper = CryptoStoreHelper()
|
||||
private val clock = DefaultClock()
|
||||
|
||||
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class DecryptRedactedEventTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun doNotFailToDecryptRedactedEvent() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val e2eRoomID = testData.roomId
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
|
||||
val roomALicePOV = aliceSession.getRoom(e2eRoomID)!!
|
||||
val timelineEvent = testHelper.sendTextMessage(roomALicePOV, "Hello", 1).first()
|
||||
val redactionReason = "Wrong Room"
|
||||
roomALicePOV.sendService().redactEvent(timelineEvent.root, redactionReason)
|
||||
|
||||
// get the event from bob
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)?.root?.isRedacted() == true
|
||||
}
|
||||
}
|
||||
|
||||
val eventBobPov = bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)!!
|
||||
|
||||
testHelper.runBlockingTest {
|
||||
try {
|
||||
val result = bobSession.cryptoService().decryptEvent(eventBobPov.root, "")
|
||||
Assert.assertEquals(
|
||||
"Unexpected redacted reason",
|
||||
redactionReason,
|
||||
result.clearEvent.toModel<Event>()?.unsignedData?.redactedEvent?.content?.get("reason")
|
||||
)
|
||||
Assert.assertEquals(
|
||||
"Unexpected Redacted event id",
|
||||
timelineEvent.eventId,
|
||||
result.clearEvent.toModel<Event>()?.unsignedData?.redactedEvent?.redacts
|
||||
)
|
||||
} catch (failure: Throwable) {
|
||||
Assert.fail("Should not throw when decrypting a redacted event")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,8 @@ import org.amshove.kluent.fail
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
@ -56,17 +58,23 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||
import org.matrix.android.sdk.mustFail
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
@Ignore("This test fails with an unhandled exception thrown from a coroutine which terminates the entire test run.")
|
||||
class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
/**
|
||||
* Simple test that create an e2ee room.
|
||||
* Some new members are added, and a message is sent.
|
||||
@ -77,9 +85,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
* Alice sends a new message, then check that the new one can be decrypted
|
||||
*/
|
||||
@Test
|
||||
fun testSendingE2EEMessages() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testSendingE2EEMessages() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -193,21 +199,12 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
otherAccounts.forEach {
|
||||
testHelper.signOutAndClose(it)
|
||||
}
|
||||
newAccount.forEach { testHelper.signOutAndClose(it) }
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKeyGossipingIsEnabledByDefault() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun testKeyGossipingIsEnabledByDefault() = runSessionTest(context()) { testHelper ->
|
||||
val session = testHelper.createAccount("alice", SessionTestParams(true))
|
||||
Assert.assertTrue("Key gossiping should be enabled by default", session.cryptoService().isKeyGossipingEnabled())
|
||||
testHelper.signOutAndClose(session)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,9 +222,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
* 9. Check that new session can decrypt
|
||||
*/
|
||||
@Test
|
||||
fun testBasicBackupImport() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testBasicBackupImport() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -339,8 +334,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
// ensure bob can now decrypt
|
||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
|
||||
testHelper.signOutAndClose(newBobSession)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -348,9 +341,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
* get them from an older one.
|
||||
*/
|
||||
@Test
|
||||
fun testSimpleGossip() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testSimpleGossip() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -444,18 +435,13 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
testHelper.signOutAndClose(newBobSession)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that if a better key is forwarded (lower index, it is then used)
|
||||
*/
|
||||
@Test
|
||||
fun testForwardBetterKey() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testForwardBetterKey() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -521,10 +507,8 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
// Confirm we can decrypt one but not the other
|
||||
testHelper.runBlockingTest {
|
||||
try {
|
||||
mustFail(message = "Should not be able to decrypt event") {
|
||||
newBobSession.cryptoService().decryptEvent(firstEventNewBobPov.root, "")
|
||||
fail("Should not be able to decrypt event")
|
||||
} catch (_: MXCryptoError) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,10 +557,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
canDecryptFirst && canDecryptSecond
|
||||
}
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
testHelper.signOutAndClose(bobSessionWithBetterKey)
|
||||
testHelper.signOutAndClose(newBobSession)
|
||||
}
|
||||
|
||||
private fun sendMessageInRoom(testHelper: CommonTestHelper, aliceRoomPOV: Room, text: String): String? {
|
||||
@ -607,9 +587,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
* Test that if a better key is forwared (lower index, it is then used)
|
||||
*/
|
||||
@Test
|
||||
fun testSelfInteractiveVerificationAndGossip() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testASelfInteractiveVerificationAndGossip() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val aliceSession = testHelper.createAccount("alice", SessionTestParams(true))
|
||||
cryptoTestHelper.bootstrapSecurity(aliceSession)
|
||||
@ -748,9 +726,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
aliceSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()!!.version,
|
||||
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()!!.version
|
||||
)
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
testHelper.signOutAndClose(aliceNewSession)
|
||||
}
|
||||
|
||||
private fun ensureMembersHaveJoined(testHelper: CommonTestHelper, aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String) {
|
||||
|
@ -30,18 +30,14 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class PreShareKeysTest : InstrumentedTest {
|
||||
|
||||
private val testHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
@Test
|
||||
fun ensure_outbound_session_happy_path() {
|
||||
fun ensure_outbound_session_happy_path() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val e2eRoomID = testData.roomId
|
||||
val aliceSession = testData.firstSession
|
||||
@ -94,7 +90,5 @@ class PreShareKeysTest : InstrumentedTest {
|
||||
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
||||
}
|
||||
}
|
||||
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
}
|
||||
|
@ -38,8 +38,7 @@ import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
||||
@ -63,8 +62,6 @@ import kotlin.coroutines.resume
|
||||
class UnwedgingTest : InstrumentedTest {
|
||||
|
||||
private lateinit var messagesReceivedByBob: List<TimelineEvent>
|
||||
private val testHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
@ -85,7 +82,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||
* -> This is automatically fixed after SDKs restarted the olm session
|
||||
*/
|
||||
@Test
|
||||
fun testUnwedging() {
|
||||
fun testUnwedging() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -240,8 +237,6 @@ class UnwedgingTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
bobTimeline.dispose()
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
|
||||
|
@ -25,7 +25,6 @@ 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
|
||||
@ -38,8 +37,8 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.isCrossSignedVerif
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import kotlin.coroutines.Continuation
|
||||
@ -50,11 +49,8 @@ import kotlin.coroutines.resume
|
||||
@LargeTest
|
||||
class XSigningTest : InstrumentedTest {
|
||||
|
||||
private val testHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
@Test
|
||||
fun test_InitializeAndStoreKeys() {
|
||||
fun test_InitializeAndStoreKeys() = runSessionTest(context()) { testHelper ->
|
||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
testHelper.doSync<Unit> {
|
||||
@ -88,7 +84,7 @@ class XSigningTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_CrossSigningCheckBobSeesTheKeys() {
|
||||
fun test_CrossSigningCheckBobSeesTheKeys() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -138,13 +134,10 @@ class XSigningTest : InstrumentedTest {
|
||||
)
|
||||
|
||||
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
fun test_CrossSigningTestAliceTrustBobNewDevice() {
|
||||
fun test_CrossSigningTestAliceTrustBobNewDevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -218,9 +211,5 @@ class XSigningTest : InstrumentedTest {
|
||||
|
||||
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
|
||||
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
testHelper.signOutAndClose(bobSession)
|
||||
testHelper.signOutAndClose(bobSession2)
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@ -42,35 +43,36 @@ import java.util.concurrent.CountDownLatch
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class EncryptionTest : InstrumentedTest {
|
||||
|
||||
private val testHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
|
||||
@Test
|
||||
fun test_EncryptionEvent() {
|
||||
performTest(roomShouldBeEncrypted = false) { room ->
|
||||
// Send an encryption Event as an Event (and not as a state event)
|
||||
room.sendService().sendEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_EncryptionStateEvent() {
|
||||
performTest(roomShouldBeEncrypted = true) { room ->
|
||||
runBlocking {
|
||||
// Send an encryption Event as a State Event
|
||||
room.stateService().sendStateEvent(
|
||||
runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
performTest(cryptoTestHelper, testHelper, roomShouldBeEncrypted = false) { room ->
|
||||
// Send an encryption Event as an Event (and not as a state event)
|
||||
room.sendService().sendEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
stateKey = "",
|
||||
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
||||
@Test
|
||||
fun test_EncryptionStateEvent() {
|
||||
runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
performTest(cryptoTestHelper, testHelper, roomShouldBeEncrypted = true) { room ->
|
||||
runBlocking {
|
||||
// Send an encryption Event as a State Event
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||
stateKey = "",
|
||||
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performTest(cryptoTestHelper: CryptoTestHelper, testHelper: CommonTestHelper, roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -109,6 +111,5 @@ class EncryptionTest : InstrumentedTest {
|
||||
room.roomCryptoService().isEncrypted() shouldBe roomShouldBeEncrypted
|
||||
it.countDown()
|
||||
}
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
@ -41,20 +41,21 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.mustFail
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
class KeyShareTests : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
@Test
|
||||
fun test_DoNotSelfShareIfNotTrusted() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun test_DoNotSelfShareIfNotTrusted() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
|
||||
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
Log.v("TEST", "=======> AliceSession 1 is ${aliceSession.sessionParams.deviceId}")
|
||||
@ -91,12 +92,10 @@ class KeyShareTests : InstrumentedTest {
|
||||
assertNotNull(receivedEvent)
|
||||
assert(receivedEvent!!.isEncrypted())
|
||||
|
||||
try {
|
||||
commonTestHelper.runBlockingTest {
|
||||
commonTestHelper.runBlockingTest {
|
||||
mustFail {
|
||||
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
|
||||
}
|
||||
fail("should fail")
|
||||
} catch (failure: Throwable) {
|
||||
}
|
||||
|
||||
val outgoingRequestsBefore = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||
@ -164,12 +163,10 @@ class KeyShareTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
commonTestHelper.runBlockingTest {
|
||||
commonTestHelper.runBlockingTest {
|
||||
mustFail {
|
||||
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
|
||||
}
|
||||
fail("should fail")
|
||||
} catch (failure: Throwable) {
|
||||
}
|
||||
|
||||
// Mark the device as trusted
|
||||
@ -194,9 +191,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
* if the key was originally shared with him
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareIfWasIntendedToBeShared() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun test_reShareIfWasIntendedToBeShared() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
@ -227,9 +222,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
* if the key was originally shared with him
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareToUnverifiedIfWasIntendedToBeShared() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun test_reShareToUnverifiedIfWasIntendedToBeShared() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
@ -266,9 +259,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
* Tests that keys reshared with own verified session are done from the earliest known index
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareFromTheEarliestKnownIndexWithOwnVerifiedSession() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun test_reShareFromTheEarliestKnownIndexWithOwnVerifiedSession() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
@ -388,10 +379,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
* Tests that we don't cancel a request to early on first forward if the index is not good enough
|
||||
*/
|
||||
@Test
|
||||
fun test_dontCancelToEarly() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
fun test_dontCancelToEarly() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
@ -442,7 +430,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
// Should get a reply from bob and not from alice
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
// Log.d("#TEST", "outgoing key requests :${aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().joinToString { it.sessionId ?: "?" }}")
|
||||
// Log.d("#TEST", "outgoing key requests :${aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().joinToString { it.sessionId ?: "?" }}")
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val bobReply = outgoing?.results?.firstOrNull { it.userId == bobSession.myUserId }
|
||||
val result = bobReply?.result
|
||||
|
@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
@ -35,21 +36,22 @@ import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.MockOkHttpInterceptor
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.mustFail
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
class WithHeldTests : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
@Test
|
||||
fun test_WithHeldUnverifiedReason() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_WithHeldUnverifiedReason() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
// =============================
|
||||
// ARRANGE
|
||||
@ -92,17 +94,19 @@ class WithHeldTests : InstrumentedTest {
|
||||
// =============================
|
||||
|
||||
// Bob should not be able to decrypt because the keys is withheld
|
||||
try {
|
||||
// .. might need to wait a bit for stability?
|
||||
testHelper.runBlockingTest {
|
||||
// .. might need to wait a bit for stability?
|
||||
testHelper.runBlockingTest {
|
||||
mustFail(
|
||||
message = "This session should not be able to decrypt",
|
||||
failureBlock = { failure ->
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
|
||||
}
|
||||
) {
|
||||
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
|
||||
}
|
||||
Assert.fail("This session should not be able to decrypt")
|
||||
} catch (failure: Throwable) {
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNAUTHORISED.value, technicalMessage)
|
||||
}
|
||||
|
||||
// Let's see if the reply we got from bob first session is unverified
|
||||
@ -133,28 +137,23 @@ class WithHeldTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
// Previous message should still be undecryptable (partially withheld session)
|
||||
try {
|
||||
// .. might need to wait a bit for stability?
|
||||
testHelper.runBlockingTest {
|
||||
// .. might need to wait a bit for stability?
|
||||
testHelper.runBlockingTest {
|
||||
mustFail(
|
||||
message = "This session should not be able to decrypt",
|
||||
failureBlock = { failure ->
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
|
||||
}) {
|
||||
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
|
||||
}
|
||||
Assert.fail("This session should not be able to decrypt")
|
||||
} catch (failure: Throwable) {
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNAUTHORISED.value, technicalMessage)
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
testHelper.signOutAndClose(bobSession)
|
||||
testHelper.signOutAndClose(bobUnverifiedSession)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_WithHeldNoOlm() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_WithHeldNoOlm() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
val aliceSession = testData.firstSession
|
||||
@ -186,17 +185,18 @@ class WithHeldTests : InstrumentedTest {
|
||||
|
||||
// Previous message should still be undecryptable (partially withheld session)
|
||||
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)
|
||||
try {
|
||||
// .. might need to wait a bit for stability?
|
||||
testHelper.runBlockingTest {
|
||||
// .. might need to wait a bit for stability?
|
||||
testHelper.runBlockingTest {
|
||||
mustFail(
|
||||
message = "This session should not be able to decrypt",
|
||||
failureBlock = { failure ->
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.NO_OLM.value, technicalMessage)
|
||||
}) {
|
||||
bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "")
|
||||
}
|
||||
Assert.fail("This session should not be able to decrypt")
|
||||
} catch (failure: Throwable) {
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.NO_OLM.value, technicalMessage)
|
||||
}
|
||||
|
||||
// Ensure that alice has marked the session to be shared with bob
|
||||
@ -230,14 +230,10 @@ class WithHeldTests : InstrumentedTest {
|
||||
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
|
||||
|
||||
aliceInterceptor.clearRules()
|
||||
testData.cleanUp(testHelper)
|
||||
testHelper.signOutAndClose(bobSecondSession)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_WithHeldKeyRequest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_WithHeldKeyRequest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
val aliceSession = testData.firstSession
|
||||
@ -284,8 +280,5 @@ class WithHeldTests : InstrumentedTest {
|
||||
wc?.code == WithHeldCode.UNAUTHORISED
|
||||
}
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
testHelper.signOutAndClose(bobSecondSession)
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
@ -43,8 +44,9 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreation
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||
import java.util.Collections
|
||||
@ -55,15 +57,15 @@ import java.util.concurrent.CountDownLatch
|
||||
@LargeTest
|
||||
class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
/**
|
||||
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
||||
* - Check backup keys after having marked one as backed up
|
||||
* - Reset keys backup markers
|
||||
*/
|
||||
@Test
|
||||
fun roomKeysTest_testBackupStore_ok() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun roomKeysTest_testBackupStore_ok() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
|
||||
@ -102,8 +104,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* Check that prepareKeysBackupVersionWithPassword returns valid data
|
||||
*/
|
||||
@Test
|
||||
fun prepareKeysBackupVersionTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun prepareKeysBackupVersionTest() = runSessionTest(context()) { testHelper ->
|
||||
|
||||
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||
|
||||
@ -113,7 +114,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
val stateObserver = StateObserver(keysBackup)
|
||||
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
|
||||
keysBackup.prepareKeysBackupVersion(null, null, it)
|
||||
@ -125,16 +126,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertNotNull(megolmBackupCreationInfo.recoveryKey)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testHelper.signOutAndClose(bobSession)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating a keys backup version and check that createKeysBackupVersion() returns valid data
|
||||
*/
|
||||
@Test
|
||||
fun createKeysBackupVersionTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun createKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||
cryptoTestHelper.initializeCrossSigning(bobSession)
|
||||
@ -143,13 +141,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
val stateObserver = StateObserver(keysBackup)
|
||||
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
|
||||
keysBackup.prepareKeysBackupVersion(null, null, it)
|
||||
}
|
||||
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
// Create the version
|
||||
val version = testHelper.doSync<KeysVersion> {
|
||||
@ -157,7 +155,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
// Backup must be enable now
|
||||
assertTrue(keysBackup.isEnabled)
|
||||
assertTrue(keysBackup.isEnabled())
|
||||
|
||||
// Check that it's signed with MSK
|
||||
val versionResult = testHelper.doSync<KeysVersionResult?> {
|
||||
@ -193,7 +191,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testHelper.signOutAndClose(bobSession)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,9 +198,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Check the backup completes
|
||||
*/
|
||||
@Test
|
||||
fun backupAfterCreateKeysBackupVersionTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun backupAfterCreateKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -238,16 +233,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||
KeysBackupState.ReadyToBackUp
|
||||
)
|
||||
)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that backupAllGroupSessions() returns valid data
|
||||
*/
|
||||
@Test
|
||||
fun backupAllGroupSessionsTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun backupAllGroupSessionsTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -281,7 +273,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,9 +284,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Compare the decrypted megolm key with the original one
|
||||
*/
|
||||
@Test
|
||||
fun testEncryptAndDecryptKeysBackupData() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testEncryptAndDecryptKeysBackupData() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -330,7 +319,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -340,9 +328,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Restore must be successful
|
||||
*/
|
||||
@Test
|
||||
fun restoreKeysBackupTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun restoreKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||
@ -428,9 +414,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - It must be trusted and must have with 2 signatures now
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun trustKeyBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Do an e2e backup to the homeserver with a recovery key
|
||||
@ -441,8 +425,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
// - Trust the backup from the new device
|
||||
testHelper.doSync<Unit> {
|
||||
@ -458,7 +442,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - Backup must be enabled on the new device, on the same version
|
||||
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
|
||||
// - Retrieve the last version from the server
|
||||
val keysVersionResult = testHelper.doSync<KeysBackupLastVersionResult> {
|
||||
@ -477,7 +461,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,9 +474,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - It must be trusted and must have with 2 signatures now
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithRecoveryKeyTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun trustKeyBackupVersionWithRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Do an e2e backup to the homeserver with a recovery key
|
||||
@ -504,8 +485,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
// - Trust the backup from the new device with the recovery key
|
||||
testHelper.doSync<Unit> {
|
||||
@ -521,7 +502,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - Backup must be enabled on the new device, on the same version
|
||||
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
|
||||
// - Retrieve the last version from the server
|
||||
val keysVersionResult = testHelper.doSync<KeysBackupLastVersionResult> {
|
||||
@ -540,7 +521,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -552,9 +532,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - The backup must still be untrusted and disabled
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Do an e2e backup to the homeserver with a recovery key
|
||||
@ -565,8 +543,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
// - Try to trust the backup from the new device with a wrong recovery key
|
||||
val latch = CountDownLatch(1)
|
||||
@ -579,11 +557,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must still see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -597,9 +574,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - It must be trusted and must have with 2 signatures now
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithPasswordTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun trustKeyBackupVersionWithPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "Password"
|
||||
@ -612,8 +587,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
// - Trust the backup from the new device with the password
|
||||
testHelper.doSync<Unit> {
|
||||
@ -629,7 +604,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - Backup must be enabled on the new device, on the same version
|
||||
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
|
||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
|
||||
// - Retrieve the last version from the server
|
||||
val keysVersionResult = testHelper.doSync<KeysBackupLastVersionResult> {
|
||||
@ -648,7 +623,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -660,9 +634,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - The backup must still be untrusted and disabled
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithWrongPasswordTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun trustKeyBackupVersionWithWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "Password"
|
||||
@ -676,8 +648,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
// - Try to trust the backup from the new device with a wrong password
|
||||
val latch = CountDownLatch(1)
|
||||
@ -690,11 +662,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// - The new device must still see the previous backup as not trusted
|
||||
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||
assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().getState())
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -704,9 +675,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - It must fail
|
||||
*/
|
||||
@Test
|
||||
fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun restoreKeysBackupWithAWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||
@ -730,8 +699,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// onSuccess may not have been called
|
||||
assertNull(importRoomKeysResult)
|
||||
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -741,9 +708,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Restore must be successful
|
||||
*/
|
||||
@Test
|
||||
fun testBackupWithPassword() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
@ -790,8 +755,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
|
||||
|
||||
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -801,9 +764,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - It must fail
|
||||
*/
|
||||
@Test
|
||||
fun restoreKeysBackupWithAWrongPasswordTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun restoreKeysBackupWithAWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
@ -830,8 +791,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// onSuccess may not have been called
|
||||
assertNull(importRoomKeysResult)
|
||||
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -841,9 +800,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Restore must be successful
|
||||
*/
|
||||
@Test
|
||||
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
@ -863,8 +820,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -874,9 +829,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - It must fail
|
||||
*/
|
||||
@Test
|
||||
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||
@ -900,8 +853,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
// onSuccess may not have been called
|
||||
assertNull(importRoomKeysResult)
|
||||
|
||||
testData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -909,9 +860,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Check the returned KeysVersionResult is trusted
|
||||
*/
|
||||
@Test
|
||||
fun testIsKeysBackupTrusted() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testIsKeysBackupTrusted() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
@ -945,7 +894,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -957,9 +905,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* -> That must fail and her backup state must be WrongBackUpVersion
|
||||
*/
|
||||
@Test
|
||||
fun testBackupWhenAnotherBackupWasCreated() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testBackupWhenAnotherBackupWasCreated() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
@ -969,7 +915,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
val stateObserver = StateObserver(keysBackup)
|
||||
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
// Wait for keys backup to be finished
|
||||
val latch0 = CountDownLatch(1)
|
||||
@ -993,7 +939,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
// - Make alice back up her keys to her homeserver
|
||||
keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||
|
||||
assertTrue(keysBackup.isEnabled)
|
||||
assertTrue(keysBackup.isEnabled())
|
||||
|
||||
testHelper.await(latch0)
|
||||
|
||||
@ -1012,11 +958,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||
testHelper.await(latch2)
|
||||
|
||||
// -> That must fail and her backup state must be WrongBackUpVersion
|
||||
assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state)
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.getState())
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1032,9 +977,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* -> It must success
|
||||
*/
|
||||
@Test
|
||||
fun testBackupAfterVerifyingADevice() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
@ -1069,7 +1012,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
// - Try to backup all in aliceSession2, it must fail
|
||||
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
|
||||
|
||||
assertFalse("Backup should not be enabled", keysBackup2.isEnabled)
|
||||
assertFalse("Backup should not be enabled", keysBackup2.isEnabled())
|
||||
|
||||
val stateObserver2 = StateObserver(keysBackup2)
|
||||
|
||||
@ -1088,8 +1031,8 @@ class KeysBackupTest : InstrumentedTest {
|
||||
assertFalse(isSuccessful)
|
||||
|
||||
// Backup state must be NotTrusted
|
||||
assertEquals("Backup state must be NotTrusted", KeysBackupState.NotTrusted, keysBackup2.state)
|
||||
assertFalse("Backup should not be enabled", keysBackup2.isEnabled)
|
||||
assertEquals("Backup state must be NotTrusted", KeysBackupState.NotTrusted, keysBackup2.getState())
|
||||
assertFalse("Backup should not be enabled", keysBackup2.isEnabled())
|
||||
|
||||
// - Validate the old device from the new one
|
||||
aliceSession2.cryptoService().setDeviceVerification(
|
||||
@ -1103,7 +1046,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
keysBackup2.addListener(object : KeysBackupStateListener {
|
||||
override fun onStateChange(newState: KeysBackupState) {
|
||||
// Check the backup completes
|
||||
if (keysBackup2.state == KeysBackupState.ReadyToBackUp) {
|
||||
if (keysBackup2.getState() == KeysBackupState.ReadyToBackUp) {
|
||||
// Remove itself from the list of listeners
|
||||
keysBackup2.removeListener(this)
|
||||
|
||||
@ -1121,12 +1064,10 @@ class KeysBackupTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
// -> It must success
|
||||
assertTrue(aliceSession2.cryptoService().keysBackupService().isEnabled)
|
||||
assertTrue(aliceSession2.cryptoService().keysBackupService().isEnabled())
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
stateObserver2.stopAndCheckStates(null)
|
||||
testHelper.signOutAndClose(aliceSession2)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1134,9 +1075,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
* - Delete the backup
|
||||
*/
|
||||
@Test
|
||||
fun deleteKeysBackupTest() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun deleteKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
@ -1146,19 +1085,18 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
val stateObserver = StateObserver(keysBackup)
|
||||
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
|
||||
|
||||
assertTrue(keysBackup.isEnabled)
|
||||
assertTrue(keysBackup.isEnabled())
|
||||
|
||||
// Delete the backup
|
||||
testHelper.doSync<Unit> { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) }
|
||||
|
||||
// Backup is now disabled
|
||||
assertFalse(keysBackup.isEnabled)
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ internal class KeysBackupTestHelper(
|
||||
|
||||
Assert.assertNotNull(megolmBackupCreationInfo)
|
||||
|
||||
Assert.assertFalse("Key backup should not be enabled before creation", keysBackup.isEnabled)
|
||||
Assert.assertFalse("Key backup should not be enabled before creation", keysBackup.isEnabled())
|
||||
|
||||
// Create the version
|
||||
val keysVersion = testHelper.doSync<KeysVersion> {
|
||||
@ -116,7 +116,7 @@ internal class KeysBackupTestHelper(
|
||||
Assert.assertNotNull("Key backup version should not be null", keysVersion.version)
|
||||
|
||||
// Backup must be enable now
|
||||
Assert.assertTrue(keysBackup.isEnabled)
|
||||
Assert.assertTrue(keysBackup.isEnabled())
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
return PrepareKeysBackupDataResult(megolmBackupCreationInfo, keysVersion.version)
|
||||
@ -128,7 +128,7 @@ internal class KeysBackupTestHelper(
|
||||
*/
|
||||
fun waitForKeysBackupToBeInState(session: Session, state: KeysBackupState) {
|
||||
// If already in the wanted state, return
|
||||
if (session.cryptoService().keysBackupService().state == state) {
|
||||
if (session.cryptoService().keysBackupService().getState() == state) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.replayattack
|
||||
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.amshove.kluent.internal.assertFailsWith
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
class ReplayAttackTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun replayAttackAlreadyDecryptedEventTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
|
||||
val e2eRoomID = cryptoTestData.roomId
|
||||
|
||||
// Alice
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!!
|
||||
|
||||
// Bob
|
||||
val bobSession = cryptoTestData.secondSession
|
||||
val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!!
|
||||
assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2)
|
||||
|
||||
// Alice will send a message
|
||||
val sentEvents = testHelper.sendTextMessage(aliceRoomPOV, "Hello I will be decrypted twice", 1)
|
||||
assertEquals(1, sentEvents.size)
|
||||
|
||||
val fakeEventId = sentEvents[0].eventId + "_fake"
|
||||
val fakeEventWithTheSameIndex =
|
||||
sentEvents[0].copy(eventId = fakeEventId, root = sentEvents[0].root.copy(eventId = fakeEventId))
|
||||
|
||||
testHelper.runBlockingTest {
|
||||
// Lets assume we are from the main timelineId
|
||||
val timelineId = "timelineId"
|
||||
// Lets decrypt the original event
|
||||
aliceSession.cryptoService().decryptEvent(sentEvents[0].root, timelineId)
|
||||
// Lets decrypt the fake event that will have the same message index
|
||||
val exception = assertFailsWith<MXCryptoError.Base> {
|
||||
// An exception should be thrown while the same index would have been used for the previous decryption
|
||||
aliceSession.cryptoService().decryptEvent(fakeEventWithTheSameIndex.root, timelineId)
|
||||
}
|
||||
assertEquals(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, exception.errorType)
|
||||
}
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun replayAttackSameEventTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
|
||||
val e2eRoomID = cryptoTestData.roomId
|
||||
|
||||
// Alice
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!!
|
||||
|
||||
// Bob
|
||||
val bobSession = cryptoTestData.secondSession
|
||||
val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!!
|
||||
assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2)
|
||||
|
||||
// Alice will send a message
|
||||
val sentEvents = testHelper.sendTextMessage(aliceRoomPOV, "Hello I will be decrypted twice", 1)
|
||||
Assert.assertTrue("Message should be sent", sentEvents.size == 1)
|
||||
assertEquals(sentEvents.size, 1)
|
||||
|
||||
testHelper.runBlockingTest {
|
||||
// Lets assume we are from the main timelineId
|
||||
val timelineId = "timelineId"
|
||||
// Lets decrypt the original event
|
||||
aliceSession.cryptoService().decryptEvent(sentEvents[0].root, timelineId)
|
||||
try {
|
||||
// Lets try to decrypt the same event
|
||||
aliceSession.cryptoService().decryptEvent(sentEvents[0].root, timelineId)
|
||||
} catch (ex: Throwable) {
|
||||
fail("Shouldn't throw a decryption error for same event")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,15 +31,16 @@ import org.matrix.android.sdk.api.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
||||
import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent
|
||||
import org.matrix.android.sdk.api.session.securestorage.KeyRef
|
||||
import org.matrix.android.sdk.api.session.securestorage.KeySigner
|
||||
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
||||
import org.matrix.android.sdk.api.session.securestorage.SecretStorageKeyContent
|
||||
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageError
|
||||
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
|
||||
import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
||||
@ -55,8 +56,7 @@ class QuadSTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_Generate4SKey() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun test_Generate4SKey() = runSessionTest(context()) { testHelper ->
|
||||
|
||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
@ -108,12 +108,11 @@ class QuadSTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_StoreSecret() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun test_StoreSecret() = runSessionTest(context()) { testHelper ->
|
||||
|
||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
val keyId = "My.Key"
|
||||
val info = generatedSecret(aliceSession, keyId, true)
|
||||
val info = generatedSecret(testHelper, aliceSession, keyId, true)
|
||||
|
||||
val keySpec = RawBytesKeySpec.fromRecoveryKey(info.recoveryKey)
|
||||
|
||||
@ -123,11 +122,11 @@ class QuadSTests : InstrumentedTest {
|
||||
aliceSession.sharedSecretStorageService().storeSecret(
|
||||
"secret.of.life",
|
||||
clearSecret,
|
||||
listOf(SharedSecretStorageService.KeyRef(null, keySpec)) // default key
|
||||
listOf(KeyRef(null, keySpec)) // default key
|
||||
)
|
||||
}
|
||||
|
||||
val secretAccountData = assertAccountData(aliceSession, "secret.of.life")
|
||||
val secretAccountData = assertAccountData(testHelper, aliceSession, "secret.of.life")
|
||||
|
||||
val encryptedContent = secretAccountData.content["encrypted"] as? Map<*, *>
|
||||
assertNotNull("Element should be encrypted", encryptedContent)
|
||||
@ -149,12 +148,10 @@ class QuadSTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_SetDefaultLocalEcho() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun test_SetDefaultLocalEcho() = runSessionTest(context()) { testHelper ->
|
||||
|
||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
@ -170,19 +167,16 @@ class QuadSTests : InstrumentedTest {
|
||||
testHelper.runBlockingTest {
|
||||
quadS.setDefaultKey(TEST_KEY_ID)
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_StoreSecretWithMultipleKey() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun test_StoreSecretWithMultipleKey() = runSessionTest(context()) { testHelper ->
|
||||
|
||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
val keyId1 = "Key.1"
|
||||
val key1Info = generatedSecret(aliceSession, keyId1, true)
|
||||
val key1Info = generatedSecret(testHelper, aliceSession, keyId1, true)
|
||||
val keyId2 = "Key2"
|
||||
val key2Info = generatedSecret(aliceSession, keyId2, true)
|
||||
val key2Info = generatedSecret(testHelper, aliceSession, keyId2, true)
|
||||
|
||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||
|
||||
@ -191,8 +185,8 @@ class QuadSTests : InstrumentedTest {
|
||||
"my.secret",
|
||||
mySecretText.toByteArray().toBase64NoPadding(),
|
||||
listOf(
|
||||
SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)),
|
||||
SharedSecretStorageService.KeyRef(keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey))
|
||||
KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)),
|
||||
KeyRef(keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -221,19 +215,16 @@ class QuadSTests : InstrumentedTest {
|
||||
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
|
||||
)
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Test is working locally, not in GitHub actions")
|
||||
fun test_GetSecretWithBadPassphrase() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
fun test_GetSecretWithBadPassphrase() = runSessionTest(context()) { testHelper ->
|
||||
|
||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
val keyId1 = "Key.1"
|
||||
val passphrase = "The good pass phrase"
|
||||
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
|
||||
val key1Info = generatedSecretFromPassphrase(testHelper, aliceSession, passphrase, keyId1, true)
|
||||
|
||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||
|
||||
@ -241,7 +232,7 @@ class QuadSTests : InstrumentedTest {
|
||||
aliceSession.sharedSecretStorageService().storeSecret(
|
||||
"my.secret",
|
||||
mySecretText.toByteArray().toBase64NoPadding(),
|
||||
listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)))
|
||||
listOf(KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)))
|
||||
)
|
||||
}
|
||||
|
||||
@ -275,13 +266,9 @@ class QuadSTests : InstrumentedTest {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSession)
|
||||
}
|
||||
|
||||
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
|
||||
private fun assertAccountData(testHelper: CommonTestHelper, session: Session, type: String): UserAccountDataEvent {
|
||||
var accountData: UserAccountDataEvent? = null
|
||||
testHelper.waitWithLatch {
|
||||
val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
|
||||
@ -297,29 +284,27 @@ class QuadSTests : InstrumentedTest {
|
||||
return accountData!!
|
||||
}
|
||||
|
||||
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||
private fun generatedSecret(testHelper: CommonTestHelper, 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)
|
||||
}
|
||||
|
||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||
assertAccountData(testHelper, session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||
|
||||
if (asDefault) {
|
||||
testHelper.runBlockingTest {
|
||||
quadS.setDefaultKey(keyId)
|
||||
}
|
||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||
assertAccountData(testHelper, session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||
}
|
||||
|
||||
return creationInfo
|
||||
}
|
||||
|
||||
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||
private fun generatedSecretFromPassphrase(testHelper: CommonTestHelper, session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||
val quadS = session.sharedSecretStorageService()
|
||||
val testHelper = CommonTestHelper(context())
|
||||
|
||||
val creationInfo = testHelper.runBlockingTest {
|
||||
quadS.generateKeyWithPassphrase(
|
||||
@ -331,12 +316,12 @@ class QuadSTests : InstrumentedTest {
|
||||
)
|
||||
}
|
||||
|
||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||
assertAccountData(testHelper, session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||
if (asDefault) {
|
||||
testHelper.runBlockingTest {
|
||||
quadS.setDefaultKey(keyId)
|
||||
}
|
||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||
assertAccountData(testHelper, session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||
}
|
||||
|
||||
return creationInfo
|
||||
|
@ -44,8 +44,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationStart
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
||||
@ -56,9 +55,7 @@ import java.util.concurrent.CountDownLatch
|
||||
class SASTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun test_aliceStartThenAliceCancel() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_aliceStartThenAliceCancel() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -135,15 +132,11 @@ class SASTest : InstrumentedTest {
|
||||
|
||||
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
|
||||
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@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)
|
||||
fun test_key_agreement_protocols_must_include_curve25519() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
fail("Not passing for the moment")
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
@ -195,15 +188,11 @@ class SASTest : InstrumentedTest {
|
||||
testHelper.await(cancelLatch)
|
||||
|
||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@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)
|
||||
fun test_key_agreement_macs_Must_include_hmac_sha256() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
fail("Not passing for the moment")
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
@ -236,15 +225,11 @@ class SASTest : InstrumentedTest {
|
||||
|
||||
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@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)
|
||||
fun test_key_agreement_short_code_include_decimal() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
fail("Not passing for the moment")
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
@ -277,8 +262,6 @@ class SASTest : InstrumentedTest {
|
||||
|
||||
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
private fun fakeBobStart(bobSession: Session,
|
||||
@ -314,9 +297,7 @@ class SASTest : InstrumentedTest {
|
||||
// any two devices may only have at most one key verification in flight at a time.
|
||||
// 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)
|
||||
fun test_aliceStartTwoRequests() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -357,9 +338,7 @@ class SASTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
fun test_aliceAndBobAgreement() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_aliceAndBobAgreement() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -413,14 +392,10 @@ class SASTest : InstrumentedTest {
|
||||
accepted!!.shortAuthenticationStrings.forEach {
|
||||
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
|
||||
}
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_aliceAndBobSASCode() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_aliceAndBobSASCode() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -473,14 +448,10 @@ class SASTest : InstrumentedTest {
|
||||
"Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
|
||||
bobTx.getShortCodeRepresentation(SasMode.DECIMAL)
|
||||
)
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_happyPath() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_happyPath() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -553,13 +524,10 @@ class SASTest : InstrumentedTest {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_ConcurrentStart() {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
fun test_ConcurrentStart() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -646,7 +614,5 @@ class SASTest : InstrumentedTest {
|
||||
bobPovTx?.state == VerificationTxState.ShortCodeReady
|
||||
}
|
||||
}
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,13 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.Continuation
|
||||
@ -152,9 +154,7 @@ class VerificationTest : InstrumentedTest {
|
||||
private fun doTest(aliceSupportedMethods: List<VerificationMethod>,
|
||||
bobSupportedMethods: List<VerificationMethod>,
|
||||
expectedResultForAlice: ExpectedResult,
|
||||
expectedResultForBob: ExpectedResult) {
|
||||
val testHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||
expectedResultForBob: ExpectedResult) = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -249,7 +249,48 @@ class VerificationTest : InstrumentedTest {
|
||||
pr.otherCanShowQrCode() shouldBe expectedResultForBob.otherCanShowQrCode
|
||||
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
|
||||
}
|
||||
}
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
@Test
|
||||
fun test_selfVerificationAcceptedCancelsItForOtherSessions() = runSessionTest(context()) { testHelper ->
|
||||
val defaultSessionParams = SessionTestParams(true)
|
||||
|
||||
val aliceSessionToVerify = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||
val aliceSessionThatVerifies = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
|
||||
val aliceSessionThatReceivesCanceledEvent = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
|
||||
|
||||
val verificationMethods = listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW)
|
||||
|
||||
val serviceOfVerified = aliceSessionToVerify.cryptoService().verificationService()
|
||||
val serviceOfVerifier = aliceSessionThatVerifies.cryptoService().verificationService()
|
||||
val serviceOfUserWhoReceivesCancellation = aliceSessionThatReceivesCanceledEvent.cryptoService().verificationService()
|
||||
|
||||
serviceOfVerifier.addListener(object : VerificationService.Listener {
|
||||
override fun verificationRequestCreated(pr: PendingVerificationRequest) {
|
||||
// Accept verification request
|
||||
serviceOfVerifier.readyPendingVerification(
|
||||
verificationMethods,
|
||||
pr.otherUserId,
|
||||
pr.transactionId!!,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
serviceOfVerified.requestKeyVerification(
|
||||
methods = verificationMethods,
|
||||
otherUserId = aliceSessionToVerify.myUserId,
|
||||
otherDevices = listOfNotNull(aliceSessionThatVerifies.sessionParams.deviceId, aliceSessionThatReceivesCanceledEvent.sessionParams.deviceId),
|
||||
)
|
||||
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val requests = serviceOfUserWhoReceivesCancellation.getExistingVerificationRequests(aliceSessionToVerify.myUserId)
|
||||
requests.any { it.cancelConclusion == CancelCode.AcceptedByAnotherDevice }
|
||||
}
|
||||
}
|
||||
|
||||
testHelper.signOutAndClose(aliceSessionToVerify)
|
||||
testHelper.signOutAndClose(aliceSessionThatVerifies)
|
||||
testHelper.signOutAndClose(aliceSessionThatReceivesCanceledEvent)
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,7 @@ import org.matrix.android.sdk.api.session.events.model.isThread
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@ -44,9 +43,7 @@ import java.util.concurrent.CountDownLatch
|
||||
class ThreadMessagingTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun reply_in_thread_should_create_a_thread() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun reply_in_thread_should_create_a_thread() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -104,9 +101,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reply_in_thread_should_create_a_thread_from_other_user() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun reply_in_thread_should_create_a_thread_from_other_user() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -179,9 +174,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reply_in_thread_to_timeline_message_multiple_times() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun reply_in_thread_to_timeline_message_multiple_times() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -244,9 +237,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun thread_summary_advanced_validation_after_multiple_messages_in_multiple_threads() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun thread_summary_advanced_validation_after_multiple_messages_in_multiple_threads() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
|
@ -38,8 +38,7 @@ import org.matrix.android.sdk.api.session.room.model.message.PollType
|
||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@ -47,9 +46,7 @@ import java.util.concurrent.CountDownLatch
|
||||
class PollAggregationTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun testAllPollUseCases() {
|
||||
val commonTestHelper = CommonTestHelper(context())
|
||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
fun testAllPollUseCases() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
@ -138,7 +135,6 @@ class PollAggregationTest : InstrumentedTest {
|
||||
|
||||
aliceSession.stopSync()
|
||||
aliceTimeline.dispose()
|
||||
cryptoTestData.cleanUp(commonTestHelper)
|
||||
}
|
||||
|
||||
private fun testInitialPollConditions(pollContent: MessagePollContent, pollSummary: PollResponseAggregatedSummary?) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user