Merge branch 'develop' of github.com:vector-im/element-android into feature/dla/fix_reply_and_quote_newlines

This commit is contained in:
David Langley 2021-12-14 13:46:45 +00:00
commit 20b5742227
727 changed files with 9855 additions and 4589 deletions

View File

@ -71,3 +71,26 @@ jobs:
- Update SAS Strings from matrix-doc. - Update SAS Strings from matrix-doc.
branch: sync-sas-strings branch: sync-sas-strings
base: develop base: develop
sync-analytics-plan:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
steps:
- uses: actions/checkout@v2
- name: Run analytics import script
run: ./tools/import_analytic_plan.sh
- name: Create Pull Request for analytics plan
uses: peter-evans/create-pull-request@v3
with:
commit-message: Sync analytics plan
title: Sync analytics plan
body: |
### Update analytics plan
Reviewers:
- [ ] Please remove usage of Event or Enum which may have been removed or updated
- [ ] please ensure new Events or new Enums are used to send analytics by pushing new commit(s) to this PR.
*Note*: Change are coming from [this project](https://github.com/matrix-org/matrix-analytics-events)
branch: sync-analytics-plan
base: develop

View File

@ -6,7 +6,7 @@ on:
jobs: jobs:
move_needs_info_issues: move_needs_info_issues:
name: Move X-Needs-Info issues to Need info on triage board name: X-Needs-Info issues to Need info column on triage board
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338 - uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
@ -17,22 +17,24 @@ jobs:
label-name: "X-Needs-Info" label-name: "X-Needs-Info"
add_priority_design_issues_to_project: add_priority_design_issues_to_project:
name: Move priority X-Needs-Design issues to Design project board name: P1 X-Needs-Design to Design project board
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: > if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Design') && contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
(contains(github.event.issue.labels.*.name, 'O-Frequent') || (contains(github.event.issue.labels.*.name, 'S-Critical') &&
contains(github.event.issue.labels.*.name, 'O-Occasional')) && (contains(github.event.issue.labels.*.name, 'O-Frequent') ||
(contains(github.event.issue.labels.*.name, 'S-Critical') || contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
contains(github.event.issue.labels.*.name, 'S-Major') || contains(github.event.issue.labels.*.name, 'S-Major') &&
contains(github.event.issue.labels.*.name, 'S-Minor')) contains(github.event.issue.labels.*.name, 'O-Frequent') ||
contains(github.event.issue.labels.*.name, 'A11y') &&
contains(github.event.issue.labels.*.name, 'O-Frequent'))
steps: steps:
- uses: octokit/graphql-action@v2.x - uses: octokit/graphql-action@v2.x
id: add_to_project id: add_to_project
with: with:
headers: '{"GraphQL-Features": "projects_next_graphql"}' headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: | query: |
mutation add_to_project($projectid:String!,$contentid:String!) { mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem { projectNextItem {
id id
@ -45,40 +47,33 @@ jobs:
PROJECT_ID: "PN_kwDOAM0swc0sUA" PROJECT_ID: "PN_kwDOAM0swc0sUA"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_spaces_issues: # delight_issues_to_board:
name: Move Spaces issues to Delight project board # name: Spaces issues to new Delight project board
runs-on: ubuntu-latest # runs-on: ubuntu-latest
if: > # if: >
contains(github.event.issue.labels.*.name, 'A-Spaces') || # contains(github.event.issue.labels.*.name, 'A-Spaces') ||
contains(github.event.issue.labels.*.name, 'A-Space-Settings') || # contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
contains(github.event.issue.labels.*.name, 'A-Subspaces') # contains(github.event.issue.labels.*.name, 'A-Subspaces')
steps: # steps:
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338 # - uses: octokit/graphql-action@v2.x
with: # with:
action-token: "${{ secrets.ELEMENT_BOT_TOKEN }}" # headers: '{"GraphQL-Features": "projects_next_graphql"}'
project-url: "https://github.com/orgs/vector-im/projects/6" # query: |
column-name: "📥 Inbox" # mutation add_to_project($projectid:ID!,$contentid:ID!) {
label-name: "A-Spaces" # addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
- uses: octokit/graphql-action@v2.x # projectNextItem {
id: add_to_delight2 # id
with: # }
headers: '{"GraphQL-Features": "projects_next_graphql"}' # }
query: | # }
mutation add_to_project($projectid:String!,$contentid:String!) { # projectid: ${{ env.PROJECT_ID }}
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { # contentid: ${{ github.event.issue.node_id }}
projectNextItem { # env:
id # PROJECT_ID: "PN_kwDOAM0swc1HvQ"
} # GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_voice-message_issues: move_voice-message_issues:
name: Move A-Voice Messages to Voice message board name: A-Voice Messages to voice message board
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: > if: >
contains(github.event.issue.labels.*.name, 'A-Voice Messages') contains(github.event.issue.labels.*.name, 'A-Voice Messages')
@ -87,7 +82,7 @@ jobs:
with: with:
headers: '{"GraphQL-Features": "projects_next_graphql"}' headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: | query: |
mutation add_to_project($projectid:String!,$contentid:String!) { mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem { projectNextItem {
id id
@ -101,7 +96,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_threads_issues: move_threads_issues:
name: Move A-Threads to Thread board name: A-Threads to Thread board
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: > if: >
contains(github.event.issue.labels.*.name, 'A-Threads') contains(github.event.issue.labels.*.name, 'A-Threads')
@ -110,7 +105,7 @@ jobs:
with: with:
headers: '{"GraphQL-Features": "projects_next_graphql"}' headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: | query: |
mutation add_to_project($projectid:String!,$contentid:String!) { mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem { projectNextItem {
id id
@ -122,3 +117,26 @@ jobs:
env: env:
PROJECT_ID: "PN_kwDOAM0swc0rRA" PROJECT_ID: "PN_kwDOAM0swc0rRA"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_message_bubbles_issues:
name: A-Message-Bubbles to Message bubbles board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles')
steps:
- uses: octokit/graphql-action@v2.x
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc3m-g"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

View File

@ -1,4 +1,4 @@
name: Move P1 issues into the P1 column for the App Team and Crypto team name: Move P1 bugs to boards
on: on:
issues: issues:

View File

@ -24,6 +24,7 @@
<w>pbkdf</w> <w>pbkdf</w>
<w>pids</w> <w>pids</w>
<w>pkcs</w> <w>pkcs</w>
<w>posthog</w>
<w>previewable</w> <w>previewable</w>
<w>previewables</w> <w>previewables</w>
<w>pstn</w> <w>pstn</w>

View File

@ -1,3 +1,35 @@
Changes in Element v1.3.9 (2021-12-01)
======================================
Features ✨
----------
- Voice messages: Persist drafts of voice messages when navigating between rooms ([#3922](https://github.com/vector-im/element-android/issues/3922))
- Make Element Android Thread aware ([#4246](https://github.com/vector-im/element-android/issues/4246))
- Iterate on the consent dialog of the identity server. ([#4577](https://github.com/vector-im/element-android/issues/4577))
Bugfixes 🐛
----------
- Fixes left over text when inserting emojis via the ':' menu and replaces the last typed ':' rather than the one at the end of the message ([#3449](https://github.com/vector-im/element-android/issues/3449))
- Fixing queued voice message failing to send or retry ([#3833](https://github.com/vector-im/element-android/issues/3833))
- Keeping device screen on whilst recording and playing back voice messages ([#4022](https://github.com/vector-im/element-android/issues/4022))
- Allow voice messages to continue recording during device rotation ([#4067](https://github.com/vector-im/element-android/issues/4067))
- Allowing users to hang up VOIP calls during the initialisation phase (avoids getting stuck in the call screen if something goes wrong) ([#4144](https://github.com/vector-im/element-android/issues/4144))
- Make the verification shields the same in Element Web and Element Android ([#4338](https://github.com/vector-im/element-android/issues/4338))
- Fix a display issue in the composer when the replied message is changed. ([#4343](https://github.com/vector-im/element-android/issues/4343))
- Dismissing the Fdroid variant Listening for notifications on sign out, fixes crash when tapping the notification when signed out ([#4488](https://github.com/vector-im/element-android/issues/4488))
- Fix a crash when displaying the bootstrap bottom sheet ([#4520](https://github.com/vector-im/element-android/issues/4520))
- Remove duplicated settings declaration ([#4539](https://github.com/vector-im/element-android/issues/4539))
- Fixes .ogg files failing to upload to rooms ([#4552](https://github.com/vector-im/element-android/issues/4552))
- Add robustness when getting data from cursors ([#4605](https://github.com/vector-im/element-android/issues/4605))
Other changes
-------------
- Upgrade Jitsi lib (and so webrtc) from Jitsi android-sdk-3.1.0 to android-sdk-3.10.0 ([#4504](https://github.com/vector-im/element-android/issues/4504))
- Improve crypto logs to help debug decryption failures ([#4507](https://github.com/vector-im/element-android/issues/4507))
- Voice recording mic button refactor with small animation tweaks in preparation for voice drafts ([#4515](https://github.com/vector-im/element-android/issues/4515))
- Remove requestModelBuild() from epoxy Controllers init{} block ([#4591](https://github.com/vector-im/element-android/issues/4591))
Changes in Element v1.3.8 (2021-11-17) Changes in Element v1.3.8 (2021-11-17)
====================================== ======================================

View File

@ -1,12 +1,11 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
apply from: 'dependencies_groups.gradle'
repositories { repositories {
google() google()
jcenter()
maven { maven {
url "https://plugins.gradle.org/m2/" url "https://plugins.gradle.org/m2/"
} }
@ -37,45 +36,50 @@ allprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint" apply plugin: "org.jlleitschuh.gradle.ktlint"
repositories { repositories {
// For olm library. This has to be declared first, to ensure that Olm library is not downloaded from another repo // For olm library.
maven {
url 'https://gitlab.matrix.org/api/v4/projects/27/packages/maven'
content {
groups.olm.regex.each { includeGroupByRegex it }
groups.olm.group.each { includeGroup it }
}
}
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
content { content {
// Use this repo only for olm library groups.jitpack.regex.each { includeGroupByRegex it }
includeGroupByRegex "org\\.matrix\\.gitlab\\.matrix-org" groups.jitpack.group.each { includeGroup it }
// And also for FilePicker
includeGroupByRegex "com\\.github\\.jaiselrahman"
// And monarchy
includeGroupByRegex "com\\.github\\.Zhuinden"
// And ucrop
includeGroupByRegex "com\\.github\\.yalantis"
// JsonViewer
includeGroupByRegex 'com\\.github\\.BillCarsonFr'
// PhotoView
includeGroupByRegex 'com\\.github\\.chrisbanes'
// PFLockScreen-Android
includeGroupByRegex 'com\\.github\\.vector-im'
// DraggableView
includeGroupByRegex 'com\\.github\\.hyuwah'
// Chat effects
includeGroupByRegex 'com\\.github\\.jetradarmobile'
includeGroupByRegex 'nl\\.dionsegijn'
// Voice RecordView
includeGroupByRegex 'com\\.github\\.Armen101'
} }
} }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Jitsi repo // Jitsi repo
maven { maven {
url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.10.0" url "https://github.com/vector-im/jitsi_libre_maven/raw/main/android-sdk-3.10.0"
// Note: to test Jitsi release you can use a local file like this: // Note: to test Jitsi release you can use a local file like this:
// url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.10.0" // url "file:///Users/bmarty/workspaces/jitsi_libre_maven/android-sdk-3.10.0"
content {
groups.jitsi.regex.each { includeGroupByRegex it }
groups.jitsi.group.each { includeGroup it }
}
}
google {
content {
groups.google.regex.each { includeGroupByRegex it }
groups.google.group.each { includeGroup it }
}
}
mavenCentral {
content {
groups.mavenCentral.regex.each { includeGroupByRegex it }
groups.mavenCentral.group.each { includeGroup it }
}
}
//noinspection JcenterRepositoryObsolete
jcenter {
content {
groups.jcenter.regex.each { includeGroupByRegex it }
groups.jcenter.group.each { includeGroup it }
}
} }
google()
mavenCentral()
jcenter()
} }
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {

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

@ -0,0 +1 @@
MSC2732: Olm fallback keys

View File

@ -1 +0,0 @@
Make Element Android Thread aware

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

@ -0,0 +1 @@
Updates URL previews to match latest designs

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

@ -0,0 +1 @@
Fixes message menu showing when copying message urls

View File

@ -1 +0,0 @@
Make the verification shields the same in Element Web and Element Android

View File

@ -1 +0,0 @@
Fix a display issue in the composer when the replied message is changed.

View File

@ -1 +0,0 @@
Upgrade Jitsi lib (and so webrtc) from Jitsi android-sdk-3.1.0 to android-sdk-3.10.0

View File

@ -1 +0,0 @@
Voice recording mic button refactor with small animation tweaks in preparation for voice drafts

View File

@ -1 +0,0 @@
Fix a crash when displaying the bootstrap bottom sheet

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

@ -0,0 +1 @@
Fix lots of integration tests by introducing TestMatrix class and MatrixWorkerFactory.

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

@ -0,0 +1 @@
Setup Analytics framework using PostHog. Analytics are disabled by default. Opt-in screen not automatically displayed yet.

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

@ -0,0 +1 @@
Fix empty Dev Tools screen issue.

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

@ -0,0 +1 @@
At the very first room search after opening the app sometimes no results are displayed

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

@ -0,0 +1 @@
There is no need to call job.cancel() when we are using viewModelScope()

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

@ -0,0 +1 @@
Cleanup the layout files

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

@ -0,0 +1 @@
Improve issue automation workflows

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

@ -0,0 +1 @@
Fix for outgoing voip call via sip bridge failing after 1 minute.

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

@ -0,0 +1 @@
Introducing feature flagging to the login and notification settings flows

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

@ -0,0 +1 @@
Update log warning for call selection during voip calls.

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

@ -0,0 +1 @@
Add a help section in the settings.

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

@ -0,0 +1 @@
Debounce some clicks

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

@ -0,0 +1 @@
Upgrade OLM to v3.2.7 and get it from our maven repository.

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

@ -0,0 +1 @@
Cleanup id ref. Use type views instead

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

@ -0,0 +1 @@
Poll Feature - Render in timeline

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

@ -0,0 +1 @@
Create a legal screen in the setting to group all the different policies.

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

@ -0,0 +1 @@
Add automation to move message bubbles issues to message bubbles board.

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

@ -0,0 +1 @@
Add explicit dependency location, regarding the several maven repository. Also update some libraries (flexbox and alerter), and do some cleanup.

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

@ -0,0 +1 @@
Fix graphql warning in issue workflow automation

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

@ -0,0 +1 @@
Fix possible crash when having identical subspaces in multiple root spaces

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

@ -0,0 +1 @@
Fix a crash in the timeline with some Emojis. Also migrate to androidx.emoji2

View File

@ -7,11 +7,11 @@ ext.versions = [
'targetCompat' : JavaVersion.VERSION_11, 'targetCompat' : JavaVersion.VERSION_11,
] ]
def gradle = "7.0.3" def gradle = "7.0.4"
// Ref: https://kotlinlang.org/releases.html // Ref: https://kotlinlang.org/releases.html
def kotlin = "1.5.31" def kotlin = "1.5.31"
def kotlinCoroutines = "1.5.2" def kotlinCoroutines = "1.5.2"
def dagger = "2.40.1" def dagger = "2.40.5"
def retrofit = "2.9.0" def retrofit = "2.9.0"
def arrow = "0.8.2" def arrow = "0.8.2"
def markwon = "4.6.2" def markwon = "4.6.2"
@ -19,7 +19,7 @@ def moshi = "1.12.0"
def lifecycle = "2.4.0" def lifecycle = "2.4.0"
def flowBinding = "1.2.0" def flowBinding = "1.2.0"
def epoxy = "4.6.2" def epoxy = "4.6.2"
def mavericks = "2.4.0" def mavericks = "2.5.0"
def glide = "4.12.0" def glide = "4.12.0"
def bigImageViewer = "1.8.1" def bigImageViewer = "1.8.1"
def jjwt = "0.11.2" def jjwt = "0.11.2"

201
dependencies_groups.gradle Normal file
View File

@ -0,0 +1,201 @@
ext.groups = [
jitpack : [
regex: [
],
group: [
'com.github.Armen101',
'com.github.BillCarsonFr',
'com.github.chrisbanes',
'com.github.hyuwah',
'com.github.jetradarmobile',
'com.github.tapadoo',
'com.github.vector-im',
'com.github.yalantis',
'com.github.Zhuinden',
]
],
olm : [
regex: [
],
group: [
'org.matrix.android',
]
],
jitsi : [
regex: [
],
group: [
'com.facebook.react',
'org.jitsi.react',
'org.webkit',
]
],
google : [
regex: [
'androidx\\..*',
'com\\.android\\.tools\\..*',
'com\\.google\\.android\\..*',
],
group: [
'com.google.firebase',
'com.android',
'com.android.tools',
]
],
mavenCentral: [
regex: [
],
group: [
'com.adevinta.android',
'com.airbnb.android',
'com.almworks.sqlite4java',
'com.arthenica',
'com.atlassian.commonmark',
'com.atlassian.pom',
'com.beust',
'com.davemorrissey.labs',
'com.dropbox.core',
'com.facebook.fresco',
'com.facebook.infer.annotation',
'com.facebook.soloader',
'com.facebook.stetho',
'com.fasterxml',
'com.fasterxml.jackson',
'com.fasterxml.jackson.core',
'com.gabrielittner.threetenbp',
'com.getkeepsafe.relinker',
'com.github.bumptech.glide',
'com.github.filippudak',
'com.github.filippudak.progresspieview',
'com.github.javaparser',
'com.github.piasy',
'com.github.shyiko.klob',
'com.google',
'com.google.auto.service',
'com.google.auto.value',
'com.google.code.findbugs',
'com.google.code.gson',
'com.google.dagger',
'com.google.devtools.ksp',
'com.google.errorprone',
'com.google.googlejavaformat',
'com.google.guava',
'com.google.j2objc',
'com.google.jimfs',
'com.google.protobuf',
'com.google.zxing',
'com.googlecode.htmlcompressor',
'com.googlecode.json-simple',
'com.googlecode.libphonenumber',
'com.ibm.icu',
'com.jakewharton.android.repackaged',
'com.jakewharton.timber',
'com.linkedin.dexmaker',
'com.nulab-inc',
'com.otaliastudios.opengl',
'com.parse.bolts',
'com.pinterest',
'com.pinterest.ktlint',
'com.posthog.android',
'com.squareup',
'com.squareup.duktape',
'com.squareup.moshi',
'com.squareup.okhttp3',
'com.squareup.okio',
'com.squareup.retrofit2',
'com.sun.activation',
'com.sun.istack',
'com.sun.xml.bind',
'com.sun.xml.bind.mvn',
'com.sun.xml.fastinfoset',
'com.thoughtworks.qdox',
'com.vanniktech',
'commons-cli',
'commons-codec',
'commons-io',
'commons-logging',
'info.picocli',
'io.arrow-kt',
'io.github.detekt.sarif4k',
'io.github.reactivecircus.flowbinding',
'io.jsonwebtoken',
'io.kindedj',
'io.mockk',
'io.noties.markwon',
'io.reactivex.rxjava2',
'io.realm',
'it.unimi.dsi',
'jakarta.activation',
'jakarta.xml.bind',
'javax.annotation',
'javax.inject',
'jline',
'jp.wasabeef',
'junit',
'me.leolin',
'me.saket',
'net.bytebuddy',
'net.java',
'net.java.dev.jna',
'net.lachlanmckee',
'net.ltgt.gradle.incap',
'net.sf.jopt-simple',
'net.sf.kxml',
'nl.dionsegijn',
'org.amshove.kluent',
'org.apache',
'org.apache.ant',
'org.apache.commons',
'org.apache.httpcomponents',
'org.apache.sanselan',
'org.bouncycastle',
'org.checkerframework',
'org.codehaus',
'org.codehaus.groovy',
'org.codehaus.mojo',
'org.eclipse.ee4j',
'org.ec4j.core',
'org.glassfish.jaxb',
'org.hamcrest',
'org.jetbrains',
'org.jetbrains.intellij.deps',
'org.jetbrains.kotlin',
'org.jetbrains.kotlinx',
'org.jsoup',
'org.junit',
'org.junit.jupiter',
'org.junit.platform',
'org.jvnet.staxex',
'org.mockito',
'org.mongodb',
'org.objenesis',
'org.opentest4j',
'org.ow2',
'org.ow2.asm',
'org.ow2.asm',
'org.reactivestreams',
'org.robolectric',
'org.slf4j',
'org.sonatype.oss',
'org.testng',
'org.threeten',
'xerces',
'xml-apis',
]
],
jcenter : [
regex: [
],
group: [
'com.amulyakhare',
'com.otaliastudios',
'com.yqritc',
// https://github.com/cmelchior/realmfieldnameshelper/issues/42
'dk.ilios',
'im.dlg',
'me.dm7.barcodescanner',
'me.gujun.android',
]
]
]

16
docs/analytics.md Normal file
View File

@ -0,0 +1,16 @@
# Analytics in Element
## Solution
Element is using PostHog to send analytics event.
We ask for the user to give consent before sending any analytics data.
## How to add a new Event
The analytics plan is shared between all Element clients. To add an Event, please open a PR to this project: https://github.com/matrix-org/matrix-analytics-events
Then, once the PR has been merged, you can run the tool `import_analytic_plan.sh` to import the plan to Element, and then you can use the new Event. Note that this tool is run by Github action once a week.
## Forks of Element
Analytics on forks are disabled by default. Please refer to AnalyticsConfig and there implementation to setup analytics on your project.

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Opravy chyb týkající se především oznámení.
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Opravy chyb!
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Hauptänderungen: Fehler bei Benachrichtigungen gefixt
Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Änderungen: Verschiedene Fehler behoben
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Main changes in this version: Add support for voice message draft. Many bugfixes!
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.9

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: erinevad veaparandused, neist enamus on seotud teavitustega.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: pinu veaparandusi!
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: افزودن پشتیبانی حضور برای اتاق‌های پیام مستقیم (یادداشت: حضور روی matrix.org از کار افتاده است). افزودن دوبارهٔ پشتیبانی اندروید خودرو.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.5

View File

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: افزودن پشتیبانی حضور برای اتاق‌های پیام مستقیم (یادداشت: حضور روی matrix.org از کار افتاده است). افزودن دوبارهٔ پشتیبانی اندروید خودرو.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.6

View File

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: رفع اشکال‌هایی عمدتاً مربوط به آگاهی‌ها.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
تغییرات عمده در این نگارش: رفع مشکل‌ها!
گزارش دگرکونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : corrections de problèmes, principalement sur les notifications
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Principaux changements pour cette version : corrections de bugs !
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Fő változás ebben a verzióban: Értesítési hibajavítások
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Főbb változtatások ebben a verzióban: Hibajavítások
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Perubahan utama di versi ini: Perbaikan bug terutama untuk notifikasinya.
Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Perubahan utama di versi ini: Beberapa perbaikan bug!
Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -26,7 +26,7 @@ Element menempatkan Anda dalam kendali dengan cara yang berbeda:
2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri 2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri
3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element 3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element
<b>Pesan terbuka dan kolaborasi</b> <b>Perpesanan dan kolaborasi terbuka</b>
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda. Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda.
<b>Sangat aman</b> <b>Sangat aman</b>

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: correzioni riguardo le notifiche.
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: correzioni di errori!
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Główne zmiany w tej wersji: Poprawki błędów dotyczące głównie powiadomień
Pełny changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.7

View File

@ -0,0 +1,42 @@
Element jest bezpiecznym komunikatorem oraz narzędziem do komunikacji w zespole które jest idealna do pracy zdalnej. Nasza aplikacja korzysta z szyfrowania end-to-end aby rozmowy wideo, udostępnianie plików oraz rozmowy głosowe były bezpieczne.
<b>Funkcje Element'a:</b>
- Zaawansowane narzędzia komunikacji online
- W pełni szyfrowane wiadomości które umożliwiają bezpieczniejszą komunikacje dla firm, a nawet dla pracowników zdalnych.
- Zdecentralizowany czat bazowany na otwartym protokole Matrix
- Bezpieczne udostępnianie plików wraz z szyfrowaniem danych podczas zarządzania projektami
- Rozmowy z Voice over IP wraz z udostępnianiem ekranu
- Prosta konfiguracja z ulubionymi narzędziami do kolaboracji, narzędziami do zarządzania projektami usługami VoIP oraz innymi aplikacjami do komunikacji w grupie
Element jest całkowicie inny od innych komunikatorów i aplikacji do kolaboracji. Pracuje na protokole Matrix, otwarto źródłowej sieci stworzonej dla bezpiecznych wiadomości i zdecentralizowanej komunikacji. Pozwala ona na własny hosting serwera dla maksymalnej własności i kontroli nad danymi oraz wiadomościami.
<b>Prywatność i szyfrowane wiadomości</b>
Element broni cie przez niechcianymi wiadomościami, kopaniem informacji oraz cenzurą. Zabezpiecza wszystkie twoje dane, wideo które pozostaje wiadome tylko dla rozmawiających przez szyfrowanie end-to-end i weryfikacją krzyżową urządzeń.
Element daje kontrole nad twoją prywatnością i umożliwia bezpieczną komunikacje z kimkolwiek w sieci Matrix, lub z innymi firmami przez narzędzia do komunikacji integrując aplikacje takie jak Slack.
<b>Element może być hostowany samemu</b>
Pozwala to na kontrolę nad twoimi wrażliwymi danymi oraz rozmowami, Element może być hostowany samemu lub pozwala wybrać dowolnego hosta bazowanego na Matrix'ie - otwarto-źródłowym standardzie, dla zdecentralizowanej komunikacji. Element daje tobie prywatność, bezpieczeństwo oraz elastyczność w integracji.
<b>Posiadaj naprawdę swoje dane</b>
Ty decydujesz gdzie trzymasz swoje dane i wiadomości. Bez ryzyka wycieku lub dostępu firm trzecich.
Element daje ci kontrolę na wiele sposobów:
1. Utwórz darmowe konto na publicznym serwerze matrix.org hostowanym przez twórców Matrix'a lub wybierz którykolwiek z tysięcy serwerów hostowanych przez wolontariuszy
2. Hostuj samemu swoje konto przez własny serwer na twojej infrastrukturze
3. Zarejestruj się na specjalnym serwerze poprzez subskrybowanie hostingu na platformie Element Martix Services
<b>Otwarte wiadomości i kolaboracja</b>
Możesz rozmawiać z kimkolwiek w sieci Matrix, nie ważne czy korzystają z Element'a, czy z innej aplikacji wspierającej protokół Matrix, a nawet z osobami korzystającymi z innych komunikatorów.
<b>Niesamowicie bezpieczny</b>
Prawdziwe szyfrowanie end-to-end (tylko osoby w konwersacji mogą odszyfrować wiadomości), a także krzyżowa weryfikacja urządzeń.
<b>Pełna komunikacja i integracja</b>
Wiadomości, rozmowy głosowe i wideo, udostępnianie plików, ekranu, a nawet integracja z botami i widżetami. Twórz pokoje, społeczności, pozostań w kontakcie i załatwiaj to co chcesz.
<b>Kontynuuj gdzie skończyłeś</b>
Pozostań zawsze w kontakcie poprzez pełną synchornizację między urządzeniami oraz w sieci na https://app.element.io
<b>Otwarto źródłowy</b>
Element Android jest otwarto-źródłowym projektem, hostowanym na platformie GitHub. Prosimy o zgłaszanie wszelkich błędów i/lub wsparcie w tworzeniu naszego projektu na https://github.com/vector-im/element-android

View File

@ -0,0 +1 @@
Grupowy komunikator - szyfrowane wiadomosci, grupowe czaty oraz rozmowy wideo

View File

@ -0,0 +1 @@
Element - Bezpieczny Komunikator

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Consertos de bugs principalmente quanto às notificações.
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Consertos de bugs!
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Opravy chýb týkajúce sa najmä oznámení.
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Opravy chýb!
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -1 +1 @@
Zabezpečené konverzácie a VoIP. Ochráňte vaše údaje pred tretími stranami. Skupinový messenger - šifrované správy, skupinové konverzácie a videohovory

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Ndreqje të metash të lidhura kryesisht me njoftimet.
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Ndryshimet kryesore në këtë version: Ndreqje të metash!
Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Huvudsakliga ändringar i den här versionen: Buggfixar som huvudsakligen rör aviseringar.
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Huvudsakliga ändringar i den här versionen: Buggfixar!
Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
Основні зміни в цій версії: виправлення помилок в основному у повідомленнях.
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
Основні зміни у цій версії: Виправлення помилок!
Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -1,7 +1,7 @@
Element — це і безпечний месенджер, і застосунок для співпраці команди, який ідеально підходить для групових бесід під час віддаленої роботи. Цей застосунок для спілкування застосовує наскрізне шифрування для забезпечення відеоконференцій, обміну файлами та голосових викликів. Element — це і безпечний месенджер, і застосунок для співпраці команди, який ідеально підходить спілкування групами під час віддаленої роботи. Цей застосунок для спілкування використовує наскрізне шифрування для забезпечення відеоконференцій, обміну файлами та голосових викликів.
<b>Можливості Element включають:</b> <b>Можливості Element включають:</b>
- Розширені засоби спілкування в Інтернеті - Розширені засоби онлайн-спілкування
- Повністю зашифровані повідомлення для надання можливості безпечнішого корпоративного спілкування, навіть для віддалених працівників - Повністю зашифровані повідомлення для надання можливості безпечнішого корпоративного спілкування, навіть для віддалених працівників
- Децентралізований чат на основі відкритого коду Matrix - Децентралізований чат на основі відкритого коду Matrix
- Безпечний обмін файлами із зашифрованими даними для керування проєктами - Безпечний обмін файлами із зашифрованими даними для керування проєктами
@ -33,10 +33,10 @@ Element надає такі можливості на вибір:
Справжнє наскрізне шифрування (лише учасники бесіди можуть розшифровувати повідомлення) та взаємне підписування пристроїв. Справжнє наскрізне шифрування (лише учасники бесіди можуть розшифровувати повідомлення) та взаємне підписування пристроїв.
<b>Повноцінні спілкування та інтеграція</b> <b>Повноцінні спілкування та інтеграція</b>
Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та розширень. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання. Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та віджетів. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання.
<b>Продовжуйте, де зупинилися</b> <b>Продовжуйте, де зупинилися</b>
Залишайтеся на зв'язку, де б ви не знаходились, з повністю синхронізованою історією повідомлень на всіх своїх пристроях та в Інтернеті за адресою https://app.element.io Залишайтеся на зв'язку, де б ви не знаходились, з повністю синхронізованою історією повідомлень на всіх своїх пристроях та в Інтернеті за адресою https://app.element.io
<b>Відкритий код</b> <b>Відкритий код</b>
Element для Android це проєкт з відкритим кодом, розміщений GitHub. Будь ласка, повідомте про помилки та/або сприяйте його розвитку на https://github.com/vector-im/element-android Element для Android це проєкт з відкритим кодом, розміщений на GitHub. Повідомляйте про помилки та/або допомагайте його розвитку на https://github.com/vector-im/element-android

View File

@ -0,0 +1,2 @@
此版本的主要变化:主要关于通知的错误修复。
完整更新日志https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
此版本主要变化Bug 修复!
完整更新日志https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -0,0 +1,2 @@
此版本中的主要變動:主要關於通知的臭蟲修復。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2

View File

@ -0,0 +1,2 @@
此版本中的主要變動:臭蟲修復!
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.3.8

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=00b273629df4ce46e68df232161d5a7c4e495b9a029ce6e0420f071e21316867 distributionSha256Sum=b75392c5625a88bccd58a574552a5a323edca82dab5942d2d41097f809c6bcce
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -34,7 +34,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout"> app:layout_constraintTop_toBottomOf="@id/appBarLayout">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -334,7 +334,6 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox" style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -5,7 +5,6 @@
<item name="android:visibility">visible</item> <item name="android:visibility">visible</item>
</style> </style>
<style name="Theme.Debug.Light" parent="Theme.MaterialComponents.Light.NoActionBar"> <style name="Theme.Debug.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- Keep all default value --> <!-- Keep all default value -->
</style> </style>

View File

@ -11,5 +11,3 @@
<changeImageTransform /> <changeImageTransform />
</transitionSet> </transitionSet>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="width_percent">0.6</dimen>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="width_percent">0.5</dimen>
</resources>

View File

@ -32,7 +32,6 @@
<dimen name="call_pip_width">88dp</dimen> <dimen name="call_pip_width">88dp</dimen>
<dimen name="call_pip_radius">8dp</dimen> <dimen name="call_pip_radius">8dp</dimen>
<dimen name="item_form_min_height">76dp</dimen> <dimen name="item_form_min_height">76dp</dimen>
<!-- Max width for some buttons --> <!-- Max width for some buttons -->
@ -41,4 +40,6 @@
<!-- Navigation Drawer --> <!-- Navigation Drawer -->
<dimen name="navigation_drawer_max_width">320dp</dimen> <dimen name="navigation_drawer_max_width">320dp</dimen>
<!-- Preview Url -->
<dimen name="preview_url_view_corner_radius">8dp</dimen>
</resources> </resources>

View File

@ -20,7 +20,6 @@
<color name="palette_prune">#5C56F5</color> <color name="palette_prune">#5C56F5</color>
<color name="palette_links">#0086E6</color> <color name="palette_links">#0086E6</color>
<!-- For light themes --> <!-- For light themes -->
<color name="palette_gray_25">#F4F6FA</color> <color name="palette_gray_25">#F4F6FA</color>
<color name="palette_gray_50">#E3E8F0</color> <color name="palette_gray_50">#E3E8F0</color>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<declare-styleable name="BadgeFloatingActionButton"> <declare-styleable name="BadgeFloatingActionButton">

View File

@ -49,7 +49,6 @@
<item name="android:backgroundTint">@android:color/black</item> <item name="android:backgroundTint">@android:color/black</item>
</style> </style>
<style name="Widget.Vector.Button.Outlined.SocialLogin.Facebook"> <style name="Widget.Vector.Button.Outlined.SocialLogin.Facebook">
<item name="icon">@drawable/ic_social_facebook</item> <item name="icon">@drawable/ic_social_facebook</item>
</style> </style>
@ -68,7 +67,6 @@
<item name="android:backgroundTint">#3877EA</item> <item name="android:backgroundTint">#3877EA</item>
</style> </style>
<style name="Widget.Vector.Button.Outlined.SocialLogin.Twitter"> <style name="Widget.Vector.Button.Outlined.SocialLogin.Twitter">
<item name="icon">@drawable/ic_social_twitter</item> <item name="icon">@drawable/ic_social_twitter</item>
</style> </style>
@ -85,7 +83,6 @@
<item name="android:backgroundTint">#5D9EC9</item> <item name="android:backgroundTint">#5D9EC9</item>
</style> </style>
<style name="Widget.Vector.Button.Outlined.SocialLogin.Apple"> <style name="Widget.Vector.Button.Outlined.SocialLogin.Apple">
<item name="icon">@drawable/ic_social_apple</item> <item name="icon">@drawable/ic_social_apple</item>
</style> </style>
@ -118,5 +115,4 @@
<item name="android:backgroundTint">@android:color/black</item> <item name="android:backgroundTint">@android:color/black</item>
</style> </style>
</resources> </resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="width_percent">1</dimen>
</resources>

View File

@ -9,7 +9,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath "io.realm:realm-gradle-plugin:10.8.1" classpath "io.realm:realm-gradle-plugin:10.9.0"
} }
} }
@ -31,7 +31,7 @@ android {
// that the app's state is completely cleared between tests. // that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true' testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.3.9\"" buildConfigField "String", "SDK_VERSION", "\"1.3.10\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
@ -115,7 +115,7 @@ dependencies {
implementation libs.squareup.retrofit implementation libs.squareup.retrofit
implementation libs.squareup.retrofitMoshi implementation libs.squareup.retrofitMoshi
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.2")) implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
implementation 'com.squareup.okhttp3:okhttp' implementation 'com.squareup.okhttp3:okhttp'
implementation 'com.squareup.okhttp3:logging-interceptor' implementation 'com.squareup.okhttp3:logging-interceptor'
implementation 'com.squareup.okhttp3:okhttp-urlconnection' implementation 'com.squareup.okhttp3:okhttp-urlconnection'
@ -140,8 +140,8 @@ dependencies {
implementation libs.arrow.core implementation libs.arrow.core
implementation libs.arrow.instances implementation libs.arrow.instances
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm // olm lib is now hosted by maven at https://gitlab.matrix.org/api/v4/projects/27/packages/maven
implementation 'org.matrix.gitlab.matrix-org:olm:3.2.4' implementation 'org.matrix.android:olm:3.2.7'
// DI // DI
implementation libs.dagger.dagger implementation libs.dagger.dagger
@ -158,10 +158,10 @@ dependencies {
implementation libs.apache.commonsImaging implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber // Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.37' implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.39'
testImplementation libs.tests.junit testImplementation libs.tests.junit
testImplementation 'org.robolectric:robolectric:4.7.1' testImplementation 'org.robolectric:robolectric:4.7.3'
//testImplementation 'org.robolectric:shadows-support-v4:3.0' //testImplementation 'org.robolectric:shadows-support-v4:3.0'
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
testImplementation libs.mockk.mockk testImplementation libs.mockk.mockk

View File

@ -20,9 +20,10 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.test.internal.runner.junit4.statement.UiThreadStatement import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -30,7 +31,6 @@ import kotlinx.coroutines.withTimeout
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@ -45,7 +45,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.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.sync.SyncState
import java.util.ArrayList import timber.log.Timber
import java.util.UUID import java.util.UUID
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -56,13 +56,14 @@ import java.util.concurrent.TimeUnit
*/ */
class CommonTestHelper(context: Context) { class CommonTestHelper(context: Context) {
val matrix: Matrix internal val matrix: TestMatrix
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
init { init {
UiThreadStatement.runOnUiThread { UiThreadStatement.runOnUiThread {
Matrix.initialize( TestMatrix.initialize(
context, context,
MatrixConfiguration( MatrixConfiguration(
applicationFlavor = "TestFlavor", applicationFlavor = "TestFlavor",
@ -70,7 +71,7 @@ class CommonTestHelper(context: Context) {
) )
) )
} }
matrix = Matrix.getInstance(context) matrix = TestMatrix.getInstance(context)
} }
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session { fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {
@ -95,33 +96,47 @@ class CommonTestHelper(context: Context) {
* *
* @param session the session to sync * @param session the session to sync
*/ */
@Suppress("EXPERIMENTAL_API_USAGE") fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) {
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
val lock = CountDownLatch(1) val lock = CountDownLatch(1)
coroutineScope.launch {
val job = GlobalScope.launch(Dispatchers.Main) { session.startSync(true)
session.open() val syncLiveData = session.getSyncStateLive()
} val syncObserver = object : Observer<SyncState> {
runBlocking { job.join() } override fun onChanged(t: SyncState?) {
if (session.hasAlreadySynced()) {
session.startSync(true) lock.countDown()
syncLiveData.removeObserver(this)
val syncLiveData = runBlocking(Dispatchers.Main) { }
session.getSyncStateLive()
}
val syncObserver = object : Observer<SyncState> {
override fun onChanged(t: SyncState?) {
if (session.hasAlreadySynced()) {
lock.countDown()
syncLiveData.removeObserver(this)
} }
} }
syncLiveData.observeForever(syncObserver)
} }
GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) }
await(lock, timeout) await(lock, timeout)
} }
/**
* This methods clear the cache and waits for initialSync
*
* @param session the session to sync
*/
fun clearCacheAndSync(session: Session, timeout: Long = TestConstants.timeOutMillis) {
waitWithLatch(timeout) { latch ->
session.clearCache()
val syncLiveData = session.getSyncStateLive()
val syncObserver = object : Observer<SyncState> {
override fun onChanged(t: SyncState?) {
if (session.hasAlreadySynced()) {
Timber.v("Clear cache and synced")
syncLiveData.removeObserver(this)
latch.countDown()
}
}
}
syncLiveData.observeForever(syncObserver)
session.startSync(true)
}
}
/** /**
* Sends text messages in a room * Sends text messages in a room
* *
@ -130,46 +145,57 @@ class CommonTestHelper(context: Context) {
* @param nbOfMessages the number of time the message will be sent * @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> { fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
val timeline = room.createTimeline(null, TimelineSettings(10))
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages) val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(1) val timeline = room.createTimeline(null, TimelineSettings(10))
val timelineListener = object : Timeline.Listener { timeline.start()
override fun onTimelineFailure(throwable: Throwable) { waitWithLatch(timeout + 1_000L * nbOfMessages) { latch ->
} val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
override fun onNewTimelineEvents(eventIds: List<String>) { override fun onNewTimelineEvents(eventIds: List<String>) {
// noop // noop
} }
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) { override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val newMessages = snapshot val newMessages = snapshot
.filter { it.root.sendState == SendState.SYNCED } .filter { it.root.sendState == SendState.SYNCED }
.filter { it.root.getClearType() == EventType.MESSAGE } .filter { it.root.getClearType() == EventType.MESSAGE }
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true } .filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
if (newMessages.size == nbOfMessages) { Timber.v("New synced message size: ${newMessages.size}")
sentEvents.addAll(newMessages) if (newMessages.size == nbOfMessages) {
// Remove listener now, if not at the next update sendEvents could change sentEvents.addAll(newMessages)
timeline.removeListener(this) // Remove listener now, if not at the next update sendEvents could change
latch.countDown() timeline.removeListener(this)
latch.countDown()
}
} }
} }
timeline.addListener(timelineListener)
sendTextMessagesBatched(room, message, nbOfMessages)
} }
timeline.start()
timeline.addListener(timelineListener)
for (i in 0 until nbOfMessages) {
room.sendTextMessage(message + " #" + (i + 1))
}
// Wait 3 second more per message
await(latch, timeout = timeout + 3_000L * nbOfMessages)
timeline.dispose() timeline.dispose()
// Check that all events has been created // Check that all events has been created
assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong()) assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong())
return sentEvents return sentEvents
} }
/**
* Will send nb of messages provided by count parameter but waits a bit every 10 messages to avoid gap in sync
*/
private fun sendTextMessagesBatched(room: Room, message: String, count: Int) {
(1 until count + 1)
.map { "$message #$it" }
.chunked(10)
.forEach { batchedMessages ->
batchedMessages.forEach { formattedMessage ->
room.sendTextMessage(formattedMessage)
}
Thread.sleep(1_000L)
}
}
// PRIVATE METHODS ***************************************************************************** // PRIVATE METHODS *****************************************************************************
/** /**
@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) {
assertTrue(registrationResult is RegistrationResult.Success) assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session val session = (registrationResult as RegistrationResult.Success).session
session.open()
if (sessionTestParams.withInitialSync) { if (sessionTestParams.withInitialSync) {
syncSession(session, 60_000) syncSession(session, 60_000)
} }
return session return session
} }
@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) {
.getLoginWizard() .getLoginWizard()
.login(userName, password, "myDevice") .login(userName, password, "myDevice")
} }
session.open()
if (sessionTestParams.withInitialSync) { if (sessionTestParams.withInitialSync) {
syncSession(session) syncSession(session)
} }
@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) {
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)) assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
} }
@Suppress("EXPERIMENTAL_API_USAGE") suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { while (true) {
GlobalScope.launch { delay(1000)
while (true) { if (condition()) {
delay(1000) latch.countDown()
if (condition()) { return
latch.countDown()
return@launch
}
} }
} }
} }
fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, block: (CountDownLatch) -> Unit) { fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, dispatcher: CoroutineDispatcher = Dispatchers.Main, block: suspend (CountDownLatch) -> Unit) {
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
block(latch) coroutineScope.launch(dispatcher) {
block(latch)
}
await(latch, timeout) await(latch, timeout)
} }

View File

@ -19,10 +19,6 @@ package org.matrix.android.sdk.common
import android.os.SystemClock import android.os.SystemClock
import android.util.Log import android.util.Log
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
@ -31,6 +27,7 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
@ -44,16 +41,16 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import java.util.UUID import java.util.UUID
import java.util.concurrent.CountDownLatch
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { class CryptoTestHelper(private val testHelper: CommonTestHelper) {
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!") 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.") private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
@ -64,27 +61,33 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
* @return alice session * @return alice session
*/ */
fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData { fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
val roomId = mTestHelper.runBlockingTest { val roomId = testHelper.runBlockingTest {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
} }
if (encryptedRoom) { if (encryptedRoom) {
val room = aliceSession.getRoom(roomId)!! testHelper.waitWithLatch { latch ->
val room = aliceSession.getRoom(roomId)!!
mTestHelper.runBlockingTest {
room.enableEncryption() room.enableEncryption()
val roomSummaryLive = room.getRoomSummaryLive()
val roomSummaryObserver = object : Observer<Optional<RoomSummary>> {
override fun onChanged(roomSummary: Optional<RoomSummary>) {
if (roomSummary.getOrNull()?.isEncrypted.orFalse()) {
roomSummaryLive.removeObserver(this)
latch.countDown()
}
}
}
roomSummaryLive.observeForever(roomSummaryObserver)
} }
} }
return CryptoTestData(roomId, listOf(aliceSession)) return CryptoTestData(roomId, listOf(aliceSession))
} }
/** /**
* @return alice and bob sessions * @return alice and bob sessions
*/ */
@Suppress("EXPERIMENTAL_API_USAGE")
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData { fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom) val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
@ -92,54 +95,37 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
val aliceRoom = aliceSession.getRoom(aliceRoomId)!! val aliceRoom = aliceSession.getRoom(aliceRoomId)!!
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) val bobSession = testHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
val lock1 = CountDownLatch(1) testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { val newRoomObserver = object : Observer<List<RoomSummary>> {
bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) override fun onChanged(t: List<RoomSummary>?) {
} if (t?.isNotEmpty() == true) {
bobRoomSummariesLive.removeObserver(this)
val newRoomObserver = object : Observer<List<RoomSummary>> { latch.countDown()
override fun onChanged(t: List<RoomSummary>?) { }
if (t?.isNotEmpty() == true) {
lock1.countDown()
bobRoomSummariesLive.removeObserver(this)
} }
} }
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver) bobRoomSummariesLive.observeForever(newRoomObserver)
}
mTestHelper.runBlockingTest {
aliceRoom.invite(bobSession.myUserId) aliceRoom.invite(bobSession.myUserId)
} }
mTestHelper.await(lock1) testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
val lock = CountDownLatch(1) val roomJoinedObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) {
val roomJoinedObserver = object : Observer<List<RoomSummary>> { if (bobSession.getRoom(aliceRoomId)
override fun onChanged(t: List<RoomSummary>?) { ?.getRoomMember(bobSession.myUserId)
if (bobSession.getRoom(aliceRoomId) ?.membership == Membership.JOIN) {
?.getRoomMember(aliceSession.myUserId) bobRoomSummariesLive.removeObserver(this)
?.membership == Membership.JOIN) { latch.countDown()
lock.countDown() }
bobRoomSummariesLive.removeObserver(this)
} }
} }
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(roomJoinedObserver) bobRoomSummariesLive.observeForever(roomJoinedObserver)
bobSession.joinRoom(aliceRoomId)
} }
mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) }
mTestHelper.await(lock)
// Ensure bob can send messages to the room // Ensure bob can send messages to the room
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! // val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
// assertNotNull(roomFromBobPOV.powerLevels) // assertNotNull(roomFromBobPOV.powerLevels)
@ -171,13 +157,13 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
* @Return Sam session * @Return Sam session
*/ */
fun createSamAccountAndInviteToTheRoom(room: Room): Session { fun createSamAccountAndInviteToTheRoom(room: Room): Session {
val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams)
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
room.invite(samSession.myUserId, null) room.invite(samSession.myUserId, null)
} }
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
samSession.joinRoom(room.roomId, null, emptyList()) samSession.joinRoom(room.roomId, null, emptyList())
} }
@ -194,23 +180,20 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
val bobSession = cryptoTestData.secondSession!! val bobSession = cryptoTestData.secondSession!!
bobSession.cryptoService().setWarnOnUnknownDevices(false) bobSession.cryptoService().setWarnOnUnknownDevices(false)
aliceSession.cryptoService().setWarnOnUnknownDevices(false) aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
// Alice sends a message // Alice sends a message
mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1) testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1)
// roomFromAlicePOV.sendTextMessage(messagesFromAlice[0])
// Bob send 3 messages // Bob send 3 messages
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1) testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1)
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1) testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1)
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1) testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1)
// Alice sends a message // Alice sends a message
mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1) testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1)
return cryptoTestData return cryptoTestData
} }
@ -256,60 +239,44 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
) )
} }
@Suppress("EXPERIMENTAL_API_USAGE")
fun createDM(alice: Session, bob: Session): String { fun createDM(alice: Session, bob: Session): String {
val roomId = mTestHelper.runBlockingTest { var roomId: String = ""
alice.createDirectRoom(bob.myUserId) testHelper.waitWithLatch { latch ->
} roomId = alice.createDirectRoom(bob.myUserId)
val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
mTestHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
bob.getRoomSummariesLive(roomSummaryQueryParams { })
}
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1 val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1
if (indexOfFirst != -1) { if (indexOfFirst != -1) {
latch.countDown()
bobRoomSummariesLive.removeObserver(this) bobRoomSummariesLive.removeObserver(this)
latch.countDown()
} }
} }
} }
bobRoomSummariesLive.observeForever(newRoomObserver)
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver)
}
} }
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
bob.getRoomSummariesLive(roomSummaryQueryParams { })
}
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
if (bob.getRoom(roomId) if (bob.getRoom(roomId)
?.getRoomMember(bob.myUserId) ?.getRoomMember(bob.myUserId)
?.membership == Membership.JOIN) { ?.membership == Membership.JOIN) {
latch.countDown()
bobRoomSummariesLive.removeObserver(this) bobRoomSummariesLive.removeObserver(this)
latch.countDown()
} }
} }
} }
bobRoomSummariesLive.observeForever(newRoomObserver)
GlobalScope.launch(Dispatchers.Main) { bob.joinRoom(roomId)
bobRoomSummariesLive.observeForever(newRoomObserver)
}
mTestHelper.runBlockingTest { bob.joinRoom(roomId) }
} }
return roomId return roomId
} }
fun initializeCrossSigning(session: Session) { fun initializeCrossSigning(session: Session) {
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
session.cryptoService().crossSigningService() session.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -346,8 +313,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
var bobPovTx: IncomingSasVerificationTransaction? = null var bobPovTx: IncomingSasVerificationTransaction? = null
// wait for alice to get the ready // wait for alice to get the ready
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
if (bobPovTx?.state == VerificationTxState.OnStarted) { if (bobPovTx?.state == VerificationTxState.OnStarted) {
@ -359,16 +326,16 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
} }
} }
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction
Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}") Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}")
alicePovTx?.state == VerificationTxState.ShortCodeReady alicePovTx?.state == VerificationTxState.ShortCodeReady
} }
} }
// wait for alice to get the ready // wait for alice to get the ready
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
if (bobPovTx?.state == VerificationTxState.OnStarted) { if (bobPovTx?.state == VerificationTxState.OnStarted) {
@ -383,38 +350,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
bobPovTx!!.userHasVerifiedShortCode() bobPovTx!!.userHasVerifiedShortCode()
alicePovTx!!.userHasVerifiedShortCode() alicePovTx!!.userHasVerifiedShortCode()
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
} }
} }
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
} }
} }
} }
fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData { fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
aliceSession.cryptoService().setWarnOnUnknownDevices(false) aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomId = mTestHelper.runBlockingTest { val roomId = testHelper.runBlockingTest {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
} }
val room = aliceSession.getRoom(roomId)!! val room = aliceSession.getRoom(roomId)!!
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
room.enableEncryption() room.enableEncryption()
} }
val sessions = mutableListOf(aliceSession) val sessions = mutableListOf(aliceSession)
for (index in 1 until numberOfMembers) { for (index in 1 until numberOfMembers) {
val session = mTestHelper.createAccount("User_$index", defaultSessionParams) val session = testHelper.createAccount("User_$index", defaultSessionParams)
mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
println("TEST -> " + session.myUserId + " invited") println("TEST -> " + session.myUserId + " invited")
mTestHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) } testHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) }
println("TEST -> " + session.myUserId + " joined") println("TEST -> " + session.myUserId + " joined")
sessions.add(session) sessions.add(session)
} }

View File

@ -0,0 +1,31 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.common
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
/**
* Force foreground for testing
*/
internal class TestBackgroundDetectionObserver : BackgroundDetectionObserver {
override val isInBackground: Boolean = false
override fun register(listener: BackgroundDetectionObserver.Listener) = Unit
override fun unregister(listener: BackgroundDetectionObserver.Listener) = Unit
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2020 The Matrix.org Foundation C.I.C. * Copyright (c) 2021 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api package org.matrix.android.sdk.common
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
@ -24,27 +24,27 @@ import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.network.ApiInterceptorListener import org.matrix.android.sdk.api.network.ApiInterceptorListener
import org.matrix.android.sdk.api.network.ApiPath import org.matrix.android.sdk.api.network.ApiPath
import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.common.DaggerTestMatrixComponent
import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.ApiInterceptor
import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.network.UserAgentHolder
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject import javax.inject.Inject
/** /**
* This is the main entry point to the matrix sdk. * This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent.
* To get the singleton instance, use getInstance static method.
*/ */
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) { internal class TestMatrix constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter @Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService @Inject internal lateinit var authenticationService: AuthenticationService
@ -55,15 +55,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var sessionManager: SessionManager @Inject internal lateinit var sessionManager: SessionManager
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
@Inject internal lateinit var apiInterceptor: ApiInterceptor @Inject internal lateinit var apiInterceptor: ApiInterceptor
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
init { init {
Monarchy.init(context) Monarchy.init(context)
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this) DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) { val configuration = Configuration.Builder()
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) .setExecutor(Executors.newCachedThreadPool())
} .setWorkerFactory(matrixWorkerFactory)
.build()
WorkManager.initialize(context, configuration)
uiHandler.post { uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
} }
@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
companion object { companion object {
private lateinit var instance: Matrix private lateinit var instance: TestMatrix
private val isInit = AtomicBoolean(false) private val isInit = AtomicBoolean(false)
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) { fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
if (isInit.compareAndSet(false, true)) { if (isInit.compareAndSet(false, true)) {
instance = Matrix(context.applicationContext, matrixConfiguration) instance = TestMatrix(context.applicationContext, matrixConfiguration)
} }
} }
fun getInstance(context: Context): Matrix { fun getInstance(context: Context): TestMatrix {
if (isInit.compareAndSet(false, true)) { if (isInit.compareAndSet(false, true)) {
val appContext = context.applicationContext val appContext = context.applicationContext
if (appContext is MatrixConfiguration.Provider) { if (appContext is MatrixConfiguration.Provider) {
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration() val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
instance = Matrix(appContext, matrixConfiguration) instance = TestMatrix(appContext, matrixConfiguration)
} else { } else {
throw IllegalStateException("Matrix is not initialized properly." + throw IllegalStateException("Matrix is not initialized properly." +
" You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.") " You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")

View File

@ -34,12 +34,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
NetworkModule::class, NetworkModule::class,
AuthModule::class, AuthModule::class,
RawModule::class, RawModule::class,
SystemModule::class, SystemModule::class
TestNetworkModule::class
]) ])
@MatrixScope @MatrixScope
internal interface TestMatrixComponent : MatrixComponent { internal interface TestMatrixComponent : MatrixComponent {
fun inject(matrix: TestMatrix)
@Component.Factory @Component.Factory
interface Factory { interface Factory {
fun create(@BindsInstance context: Context, fun create(@BindsInstance context: Context,

View File

@ -18,10 +18,39 @@ package org.matrix.android.sdk.common
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import org.matrix.android.sdk.internal.di.MatrixComponent import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.di.MatrixScope
import org.matrix.android.sdk.internal.session.MockHttpInterceptor
import org.matrix.android.sdk.internal.session.TestInterceptor
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
@Module @Module
internal abstract class TestModule { internal abstract class TestModule {
@Binds @Binds
abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent
@Module
companion object {
val interceptors = ArrayList<TestInterceptor>()
fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId }
@Provides
@JvmStatic
@MockHttpInterceptor
fun providesTestInterceptor(): TestInterceptor? {
return MockOkHttpInterceptor().also {
interceptors.add(it)
}
}
@Provides
@JvmStatic
@MatrixScope
fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver {
return TestBackgroundDetectionObserver()
}
}
} }

View File

@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class PreShareKeysTest : InstrumentedTest { class PreShareKeysTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun ensure_outbound_session_happy_path() { fun ensure_outbound_session_happy_path() {
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
val e2eRoomID = testData.roomId val e2eRoomID = testData.roomId
val aliceSession = testData.firstSession val aliceSession = testData.firstSession
val bobSession = testData.secondSession!! val bobSession = testData.secondSession!!
@ -58,12 +58,12 @@ class PreShareKeysTest : InstrumentedTest {
Log.d("#Test", "Room Key Received from alice $preShareCount") Log.d("#Test", "Room Key Received from alice $preShareCount")
// Force presharing of new outbound key // Force presharing of new outbound key
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it) aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it)
} }
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count { val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
it.senderId == aliceSession.myUserId && it.senderId == aliceSession.myUserId &&
it.getClearType() == EventType.ROOM_KEY it.getClearType() == EventType.ROOM_KEY
@ -88,16 +88,16 @@ class PreShareKeysTest : InstrumentedTest {
assertEquals("The session received by bob should match what alice sent", 0, sharedIndex) assertEquals("The session received by bob should match what alice sent", 0, sharedIndex)
// Just send a real message as test // Just send a real message as test
val sentEvent = mTestHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first() val sentEvent = testHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first()
assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session") assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
} }
} }
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
} }
} }

View File

@ -62,8 +62,8 @@ import kotlin.coroutines.resume
class UnwedgingTest : InstrumentedTest { class UnwedgingTest : InstrumentedTest {
private lateinit var messagesReceivedByBob: List<TimelineEvent> private lateinit var messagesReceivedByBob: List<TimelineEvent>
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Before @Before
fun init() { fun init() {
@ -85,7 +85,7 @@ class UnwedgingTest : InstrumentedTest {
*/ */
@Test @Test
fun testUnwedging() { fun testUnwedging() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId val aliceRoomId = cryptoTestData.roomId
@ -133,7 +133,7 @@ class UnwedgingTest : InstrumentedTest {
roomFromAlicePOV.sendTextMessage("First message") roomFromAlicePOV.sendTextMessage("First message")
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
mTestHelper.await(latch) testHelper.await(latch)
bobTimeline.removeListener(bobEventsListener) bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 1 messagesReceivedByBob.size shouldBe 1
@ -161,7 +161,7 @@ class UnwedgingTest : InstrumentedTest {
roomFromAlicePOV.sendTextMessage("Second message") roomFromAlicePOV.sendTextMessage("Second message")
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
mTestHelper.await(latch) testHelper.await(latch)
bobTimeline.removeListener(bobEventsListener) bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 2 messagesReceivedByBob.size shouldBe 2
@ -179,7 +179,7 @@ class UnwedgingTest : InstrumentedTest {
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId) aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
bobEventsListener = createEventListener(it, 3) bobEventsListener = createEventListener(it, 3)
bobTimeline.addListener(bobEventsListener) bobTimeline.addListener(bobEventsListener)
messagesReceivedByBob = emptyList() messagesReceivedByBob = emptyList()
@ -201,11 +201,11 @@ class UnwedgingTest : InstrumentedTest {
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType()) Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType())
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType()) Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType())
// Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged // Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged
mTestHelper.await(bobFinalLatch) testHelper.await(bobFinalLatch)
bobTimeline.removeListener(bobHasThreeDecryptedEventsListener) bobTimeline.removeListener(bobHasThreeDecryptedEventsListener)
// It's a trick to force key request on fail to decrypt // It's a trick to force key request on fail to decrypt
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService() bobSession.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -222,8 +222,8 @@ class UnwedgingTest : InstrumentedTest {
} }
// Wait until we received back the key // Wait until we received back the key
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
// we should get back the key and be able to decrypt // we should get back the key and be able to decrypt
val result = tryOrNull { val result = tryOrNull {
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "") bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
@ -235,7 +235,7 @@ class UnwedgingTest : InstrumentedTest {
bobTimeline.dispose() bobTimeline.dispose()
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener { private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {

View File

@ -45,14 +45,14 @@ import kotlin.coroutines.resume
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
class XSigningTest : InstrumentedTest { class XSigningTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun test_InitializeAndStoreKeys() { fun test_InitializeAndStoreKeys() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService() aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(object : UserInteractiveAuthInterceptor { .initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
@ -79,12 +79,12 @@ class XSigningTest : InstrumentedTest {
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified()) assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
} }
@Test @Test
fun test_CrossSigningCheckBobSeesTheKeys() { fun test_CrossSigningCheckBobSeesTheKeys() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -98,21 +98,21 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD password = TestConstants.PASSWORD
) )
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams) promise.resume(aliceAuthParams)
} }
}, it) }, it)
} }
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(bobAuthParams) promise.resume(bobAuthParams)
} }
}, it) } }, it) }
// Check that alice can see bob keys // Check that alice can see bob keys
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey()) assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey())
@ -124,13 +124,13 @@ class XSigningTest : InstrumentedTest {
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted()) assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
} }
@Test @Test
fun test_CrossSigningTestAliceTrustBobNewDevice() { fun test_CrossSigningTestAliceTrustBobNewDevice() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -144,12 +144,12 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD password = TestConstants.PASSWORD
) )
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { testHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams) promise.resume(aliceAuthParams)
} }
}, it) } }, it) }
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(bobAuthParams) promise.resume(bobAuthParams)
} }
@ -157,21 +157,21 @@ class XSigningTest : InstrumentedTest {
// Check that alice can see bob keys // Check that alice can see bob keys
val bobUserId = bobSession.myUserId val bobUserId = bobSession.myUserId
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false) assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } testHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) }
// Now bobs logs in on a new device and verifies it // Now bobs logs in on a new device and verifies it
// We will want to test that in alice POV, this new device would be trusted by cross signing // We will want to test that in alice POV, this new device would be trusted by cross signing
val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true)) val bobSession2 = testHelper.logIntoAccount(bobUserId, SessionTestParams(true))
val bobSecondDeviceId = bobSession2.sessionParams.deviceId!! val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
// Check that bob first session sees the new login // Check that bob first session sees the new login
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { val data = testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
} }
@ -183,12 +183,12 @@ class XSigningTest : InstrumentedTest {
assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice) assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice)
// Manually mark it as trusted from first session // Manually mark it as trusted from first session
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it) bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
} }
// Now alice should cross trust bob's second device // Now alice should cross trust bob's second device
val data2 = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { val data2 = testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
} }
@ -200,8 +200,8 @@ class XSigningTest : InstrumentedTest {
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
mTestHelper.signOutAndClose(bobSession2) testHelper.signOutAndClose(bobSession2)
} }
} }

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