diff --git a/.github/workflows/sync-from-external-sources.yml b/.github/workflows/sync-from-external-sources.yml
index 2da8e10542..5a5d8152ff 100644
--- a/.github/workflows/sync-from-external-sources.yml
+++ b/.github/workflows/sync-from-external-sources.yml
@@ -70,4 +70,27 @@ jobs:
body: |
- Update SAS Strings from matrix-doc.
branch: sync-sas-strings
+ 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
\ No newline at end of file
diff --git a/.github/workflows/triage-move-labelled.yml b/.github/workflows/triage-move-labelled.yml
index f910cdf7ea..67c4e9dbab 100644
--- a/.github/workflows/triage-move-labelled.yml
+++ b/.github/workflows/triage-move-labelled.yml
@@ -6,7 +6,7 @@ on:
jobs:
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
steps:
- uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
@@ -17,22 +17,24 @@ jobs:
label-name: "X-Needs-Info"
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
if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
- (contains(github.event.issue.labels.*.name, 'O-Frequent') ||
- contains(github.event.issue.labels.*.name, 'O-Occasional')) &&
- (contains(github.event.issue.labels.*.name, 'S-Critical') ||
- contains(github.event.issue.labels.*.name, 'S-Major') ||
- contains(github.event.issue.labels.*.name, 'S-Minor'))
+ (contains(github.event.issue.labels.*.name, 'S-Critical') &&
+ (contains(github.event.issue.labels.*.name, 'O-Frequent') ||
+ contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
+ contains(github.event.issue.labels.*.name, 'S-Major') &&
+ contains(github.event.issue.labels.*.name, 'O-Frequent') ||
+ contains(github.event.issue.labels.*.name, 'A11y') &&
+ contains(github.event.issue.labels.*.name, 'O-Frequent'))
steps:
- uses: octokit/graphql-action@v2.x
id: add_to_project
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
- mutation add_to_project($projectid:String!,$contentid:String!) {
+ mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
@@ -45,40 +47,33 @@ jobs:
PROJECT_ID: "PN_kwDOAM0swc0sUA"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
- move_spaces_issues:
- name: Move Spaces issues to Delight project board
- runs-on: ubuntu-latest
- if: >
- contains(github.event.issue.labels.*.name, 'A-Spaces') ||
- contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
- contains(github.event.issue.labels.*.name, 'A-Subspaces')
- steps:
- - uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
- with:
- action-token: "${{ secrets.ELEMENT_BOT_TOKEN }}"
- project-url: "https://github.com/orgs/vector-im/projects/6"
- column-name: "📥 Inbox"
- label-name: "A-Spaces"
- - uses: octokit/graphql-action@v2.x
- id: add_to_delight2
- with:
- headers: '{"GraphQL-Features": "projects_next_graphql"}'
- query: |
- mutation add_to_project($projectid:String!,$contentid:String!) {
- addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
- projectNextItem {
- id
- }
- }
- }
- projectid: ${{ env.PROJECT_ID }}
- contentid: ${{ github.event.issue.node_id }}
- env:
- PROJECT_ID: "PN_kwDOAM0swc1HvQ"
- GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
+# delight_issues_to_board:
+# name: Spaces issues to new Delight project board
+# runs-on: ubuntu-latest
+# if: >
+# contains(github.event.issue.labels.*.name, 'A-Spaces') ||
+# contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
+# contains(github.event.issue.labels.*.name, 'A-Subspaces')
+# 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_kwDOAM0swc1HvQ"
+# GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
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
if: >
contains(github.event.issue.labels.*.name, 'A-Voice Messages')
@@ -87,7 +82,7 @@ jobs:
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
- mutation add_to_project($projectid:String!,$contentid:String!) {
+ mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
@@ -101,7 +96,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_threads_issues:
- name: Move A-Threads to Thread board
+ name: A-Threads to Thread board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Threads')
@@ -110,7 +105,7 @@ jobs:
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
- mutation add_to_project($projectid:String!,$contentid:String!) {
+ mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
projectNextItem {
id
@@ -122,3 +117,26 @@ jobs:
env:
PROJECT_ID: "PN_kwDOAM0swc0rRA"
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 }}
diff --git a/.github/workflows/triage-priority-bugs.yml b/.github/workflows/triage-priority-bugs.yml
index 018bb8bb55..976879a3ae 100644
--- a/.github/workflows/triage-priority-bugs.yml
+++ b/.github/workflows/triage-priority-bugs.yml
@@ -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:
issues:
diff --git a/.idea/dictionaries/bmarty.xml b/.idea/dictionaries/bmarty.xml
index e143720aa9..a2e408b50d 100644
--- a/.idea/dictionaries/bmarty.xml
+++ b/.idea/dictionaries/bmarty.xml
@@ -24,6 +24,7 @@
pbkdf
pids
pkcs
+ posthog
previewable
previewables
pstn
diff --git a/CHANGES.md b/CHANGES.md
index 19400a38ba..e0dd3298d8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,65 @@
+Changes in Element v1.3.12 (2021-12-20)
+=======================================
+
+Bugfixes 🐛
+----------
+ - Fixing emoji related crashes on android 8.1.1 and below ([#4769](https://github.com/vector-im/element-android/issues/4769))
+
+
+Changes in Element v1.3.11 (2021-12-17)
+=======================================
+
+Bugfixes 🐛
+----------
+ - Fixing proximity sensor still being active after a call ([#2467](https://github.com/vector-im/element-android/issues/2467))
+ - Fix name and shield are truncated in the room detail screen ([#4700](https://github.com/vector-im/element-android/issues/4700))
+ - Call banner: center text vertically ([#4710](https://github.com/vector-im/element-android/issues/4710))
+ - Fixes unable to render messages by allowing them to render whilst the emoji library is initialising ([#4733](https://github.com/vector-im/element-android/issues/4733))
+ - Fix app crash uppon long press on a reply event ([#4742](https://github.com/vector-im/element-android/issues/4742))
+ - Fixes crash when launching rooms which contain emojis in the emote content on android 12+ ([#4743](https://github.com/vector-im/element-android/issues/4743))
+
+Other changes
+-------------
+ - Avoids leaking the activity windows when loading dialogs are displaying ([#4713](https://github.com/vector-im/element-android/issues/4713))
+
+
+Changes in Element v1.3.10 (2021-12-14)
+=======================================
+
+Features ✨
+----------
+ - Poll Feature - Render in timeline ([#4653](https://github.com/vector-im/element-android/issues/4653))
+ - Updates URL previews to match latest designs ([#4278](https://github.com/vector-im/element-android/issues/4278))
+ - Setup Analytics framework using PostHog. Analytics are disabled by default. Opt-in screen not automatically displayed yet. ([#4559](https://github.com/vector-im/element-android/issues/4559))
+ - Create a legal screen in the setting to group all the different policies. ([#4660](https://github.com/vector-im/element-android/issues/4660))
+ - Add a help section in the settings. ([#4638](https://github.com/vector-im/element-android/issues/4638))
+ - MSC2732: Olm fallback keys ([#3473](https://github.com/vector-im/element-android/issues/3473))
+
+Bugfixes 🐛
+----------
+ - Fixes message menu showing when copying message urls ([#4324](https://github.com/vector-im/element-android/issues/4324))
+ - Fix lots of integration tests by introducing TestMatrix class and MatrixWorkerFactory. ([#4546](https://github.com/vector-im/element-android/issues/4546))
+ - Fix empty Dev Tools screen issue. ([#4592](https://github.com/vector-im/element-android/issues/4592))
+ - Fix for outgoing voip call via sip bridge failing after 1 minute. ([#4621](https://github.com/vector-im/element-android/issues/4621))
+ - Update log warning for call selection during voip calls. ([#4636](https://github.com/vector-im/element-android/issues/4636))
+ - Fix possible crash when having identical subspaces in multiple root spaces ([#4693](https://github.com/vector-im/element-android/issues/4693))
+ - Fix a crash in the timeline with some Emojis. Also migrate to androidx.emoji2 ([#4698](https://github.com/vector-im/element-android/issues/4698))
+ - At the very first room search after opening the app sometimes no results are displayed ([#4600](https://github.com/vector-im/element-android/issues/4600))
+
+Other changes
+-------------
+ - Upgrade OLM to v3.2.7 and get it from our maven repository. ([#4647](https://github.com/vector-im/element-android/issues/4647))
+ - Add explicit dependency location, regarding the several maven repository. Also update some libraries (flexbox and alerter), and do some cleanup. ([#4670](https://github.com/vector-im/element-android/issues/4670))
+ - Introducing feature flagging to the login and notification settings flows ([#4626](https://github.com/vector-im/element-android/issues/4626))
+ - There is no need to call job.cancel() when we are using viewModelScope() ([#4602](https://github.com/vector-im/element-android/issues/4602))
+ - Debounce some clicks ([#4645](https://github.com/vector-im/element-android/issues/4645))
+ - Improve issue automation workflows ([#4617](https://github.com/vector-im/element-android/issues/4617))
+ - Add automation to move message bubbles issues to message bubbles board. ([#4666](https://github.com/vector-im/element-android/issues/4666))
+ - Fix graphql warning in issue workflow automation ([#4671](https://github.com/vector-im/element-android/issues/4671))
+ - Cleanup the layout files ([#4604](https://github.com/vector-im/element-android/issues/4604))
+ - Cleanup id ref. Use type views instead ([#4650](https://github.com/vector-im/element-android/issues/4650))
+
+
Changes in Element v1.3.9 (2021-12-01)
======================================
diff --git a/build.gradle b/build.gradle
index 51fd357e67..e17f357905 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,11 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
-
apply from: 'dependencies.gradle'
+ apply from: 'dependencies_groups.gradle'
repositories {
google()
- jcenter()
maven {
url "https://plugins.gradle.org/m2/"
}
@@ -37,50 +36,50 @@ allprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint"
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 {
url 'https://jitpack.io'
content {
- // Use this repo only for olm library
- includeGroupByRegex "org\\.matrix\\.gitlab\\.matrix-org"
- // 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'
-
- // UnifiedPush
- includeGroupByRegex 'com\\.github\\.UnifiedPush'
- // SchildiChat
- includeGroupByRegex 'com\\.github\\.SchildiChat'
-
- // Chat effects
- includeGroupByRegex 'com\\.github\\.jetradarmobile'
- includeGroupByRegex 'nl\\.dionsegijn'
-
- // Voice RecordView
- includeGroupByRegex 'com\\.github\\.Armen101'
+ groups.jitpack.regex.each { includeGroupByRegex it }
+ groups.jitpack.group.each { includeGroup it }
}
}
- maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Jitsi repo
maven {
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:
// 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 {
diff --git a/dependencies.gradle b/dependencies.gradle
index 0a806fe840..15628ebd33 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -7,11 +7,11 @@ ext.versions = [
'targetCompat' : JavaVersion.VERSION_1_8,
]
-def gradle = "7.0.3"
+def gradle = "7.0.4"
// Ref: https://kotlinlang.org/releases.html
def kotlin = "1.5.31"
def kotlinCoroutines = "1.5.2"
-def dagger = "2.40.3"
+def dagger = "2.40.5"
def retrofit = "2.9.0"
def arrow = "0.8.2"
def markwon = "4.6.2"
@@ -19,7 +19,7 @@ def moshi = "1.12.0"
def lifecycle = "2.4.0"
def flowBinding = "1.2.0"
def epoxy = "4.6.2"
-def mavericks = "2.4.0"
+def mavericks = "2.5.0"
def glide = "4.12.0"
def bigImageViewer = "1.8.1"
def jjwt = "0.11.2"
diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle
new file mode 100644
index 0000000000..a722f4ed8a
--- /dev/null
+++ b/dependencies_groups.gradle
@@ -0,0 +1,203 @@
+ext.groups = [
+ jitpack : [
+ regex: [
+ ],
+ group: [
+ 'com.github.Armen101',
+ 'com.github.BillCarsonFr',
+ 'com.github.chrisbanes',
+ 'com.github.hyuwah',
+ 'com.github.jetradarmobile',
+ 'com.github.SchildiChat',
+ 'com.github.tapadoo',
+ 'com.github.UnifiedPush',
+ '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',
+ ]
+ ]
+]
+
diff --git a/docs/analytics.md b/docs/analytics.md
new file mode 100644
index 0000000000..135ace81b0
--- /dev/null
+++ b/docs/analytics.md
@@ -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.
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103070.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103070.txt
index d2fc874132..8e64ade8ab 100644
--- a/fastlane/metadata/android/cs-CZ/changelogs/40103070.txt
+++ b/fastlane/metadata/android/cs-CZ/changelogs/40103070.txt
@@ -1,2 +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
+Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103080.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103080.txt
new file mode 100644
index 0000000000..4932a59d1f
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/de-DE/changelogs/40103070.txt b/fastlane/metadata/android/de-DE/changelogs/40103070.txt
index 28e2ca3d7b..cc1bb7d0ac 100644
--- a/fastlane/metadata/android/de-DE/changelogs/40103070.txt
+++ b/fastlane/metadata/android/de-DE/changelogs/40103070.txt
@@ -1,2 +1,2 @@
Hauptänderungen: Fehler bei Benachrichtigungen gefixt
-Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.7
+Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/de-DE/changelogs/40103080.txt b/fastlane/metadata/android/de-DE/changelogs/40103080.txt
new file mode 100644
index 0000000000..de326e35f8
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40103080.txt
@@ -0,0 +1,2 @@
+Änderungen: Verschiedene Fehler behoben
+Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.8
diff --git a/fastlane/metadata/android/en-US/changelogs/40103100.txt b/fastlane/metadata/android/en-US/changelogs/40103100.txt
new file mode 100644
index 0000000000..d3e7f6eb4a
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40103100.txt
@@ -0,0 +1,2 @@
+Main changes in this version: Add support for polls (in labs). New URL preview design.
+Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.10
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/40103110.txt b/fastlane/metadata/android/en-US/changelogs/40103110.txt
new file mode 100644
index 0000000000..c28b303a35
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40103110.txt
@@ -0,0 +1,2 @@
+Main changes in this version: Bug fixes!
+Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.11
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/40103120.txt b/fastlane/metadata/android/en-US/changelogs/40103120.txt
new file mode 100644
index 0000000000..90d55f5f48
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40103120.txt
@@ -0,0 +1,2 @@
+Main changes in this version: Bug fixes!
+Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.12
\ No newline at end of file
diff --git a/fastlane/metadata/android/et/changelogs/40103070.txt b/fastlane/metadata/android/et/changelogs/40103070.txt
index 5bb183d918..95bdc3c0c5 100644
--- a/fastlane/metadata/android/et/changelogs/40103070.txt
+++ b/fastlane/metadata/android/et/changelogs/40103070.txt
@@ -1,2 +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
+Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/et/changelogs/40103080.txt b/fastlane/metadata/android/et/changelogs/40103080.txt
new file mode 100644
index 0000000000..8b95682f70
--- /dev/null
+++ b/fastlane/metadata/android/et/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/fa/changelogs/40103070.txt b/fastlane/metadata/android/fa/changelogs/40103070.txt
index da717e2ac2..12579d830a 100644
--- a/fastlane/metadata/android/fa/changelogs/40103070.txt
+++ b/fastlane/metadata/android/fa/changelogs/40103070.txt
@@ -1,2 +1,2 @@
تغییرات اصلی در این نگارش: رفع اشکالهایی عمدتاً مربوط به آگاهیها.
-گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.7
+گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/fa/changelogs/40103080.txt b/fastlane/metadata/android/fa/changelogs/40103080.txt
new file mode 100644
index 0000000000..597ce33e48
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/40103080.txt
@@ -0,0 +1,2 @@
+تغییرات عمده در این نگارش: رفع مشکلها!
+گزارش دگرکونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.8
diff --git a/fastlane/metadata/android/fr-FR/changelogs/40103070.txt b/fastlane/metadata/android/fr-FR/changelogs/40103070.txt
index 5fbcb12201..7cbf8396c0 100644
--- a/fastlane/metadata/android/fr-FR/changelogs/40103070.txt
+++ b/fastlane/metadata/android/fr-FR/changelogs/40103070.txt
@@ -1,2 +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
+Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/fr-FR/changelogs/40103080.txt b/fastlane/metadata/android/fr-FR/changelogs/40103080.txt
new file mode 100644
index 0000000000..681168bab8
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103070.txt b/fastlane/metadata/android/hu-HU/changelogs/40103070.txt
index 4348b7a0fe..861e35e864 100644
--- a/fastlane/metadata/android/hu-HU/changelogs/40103070.txt
+++ b/fastlane/metadata/android/hu-HU/changelogs/40103070.txt
@@ -1,2 +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
+Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103080.txt b/fastlane/metadata/android/hu-HU/changelogs/40103080.txt
new file mode 100644
index 0000000000..d29d9061b7
--- /dev/null
+++ b/fastlane/metadata/android/hu-HU/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/id/changelogs/40103070.txt b/fastlane/metadata/android/id/changelogs/40103070.txt
index b28649e684..907fde9375 100644
--- a/fastlane/metadata/android/id/changelogs/40103070.txt
+++ b/fastlane/metadata/android/id/changelogs/40103070.txt
@@ -1,2 +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
+Changelog lengkap: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/id/changelogs/40103080.txt b/fastlane/metadata/android/id/changelogs/40103080.txt
new file mode 100644
index 0000000000..2c7e2e5050
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt
index d28ae8b004..d3bed0bf6b 100644
--- a/fastlane/metadata/android/id/full_description.txt
+++ b/fastlane/metadata/android/id/full_description.txt
@@ -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
3. Daftar untuk akun di server khusus dengan berlangganan platform hosting Layanan Matrix Element
-Pesan terbuka dan kolaborasi
+Perpesanan dan kolaborasi terbuka
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain atau bahkan menggunakan aplikasi perpesanan yang berbeda.
Sangat aman
diff --git a/fastlane/metadata/android/it-IT/changelogs/40103070.txt b/fastlane/metadata/android/it-IT/changelogs/40103070.txt
index fbf4a1b19f..d4d9551c54 100644
--- a/fastlane/metadata/android/it-IT/changelogs/40103070.txt
+++ b/fastlane/metadata/android/it-IT/changelogs/40103070.txt
@@ -1,2 +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
+Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/it-IT/changelogs/40103080.txt b/fastlane/metadata/android/it-IT/changelogs/40103080.txt
new file mode 100644
index 0000000000..52d0093b3f
--- /dev/null
+++ b/fastlane/metadata/android/it-IT/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103070.txt b/fastlane/metadata/android/pt-BR/changelogs/40103070.txt
index 216e363f24..a2b326d8a1 100644
--- a/fastlane/metadata/android/pt-BR/changelogs/40103070.txt
+++ b/fastlane/metadata/android/pt-BR/changelogs/40103070.txt
@@ -1,2 +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
+Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103080.txt b/fastlane/metadata/android/pt-BR/changelogs/40103080.txt
new file mode 100644
index 0000000000..0f2fbc2180
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/sk/changelogs/40103070.txt b/fastlane/metadata/android/sk/changelogs/40103070.txt
new file mode 100644
index 0000000000..a048c03b17
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/40103070.txt
@@ -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
diff --git a/fastlane/metadata/android/sk/changelogs/40103080.txt b/fastlane/metadata/android/sk/changelogs/40103080.txt
new file mode 100644
index 0000000000..7534c37681
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/sk/short_description.txt b/fastlane/metadata/android/sk/short_description.txt
index 0744f4a617..942bd27ca8 100644
--- a/fastlane/metadata/android/sk/short_description.txt
+++ b/fastlane/metadata/android/sk/short_description.txt
@@ -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
diff --git a/fastlane/metadata/android/sq/changelogs/40103070.txt b/fastlane/metadata/android/sq/changelogs/40103070.txt
new file mode 100644
index 0000000000..400188a8b3
--- /dev/null
+++ b/fastlane/metadata/android/sq/changelogs/40103070.txt
@@ -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
diff --git a/fastlane/metadata/android/sq/changelogs/40103080.txt b/fastlane/metadata/android/sq/changelogs/40103080.txt
new file mode 100644
index 0000000000..24d719c14c
--- /dev/null
+++ b/fastlane/metadata/android/sq/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40103070.txt b/fastlane/metadata/android/sv-SE/changelogs/40103070.txt
index 1931cbfd82..a9e9de4e8b 100644
--- a/fastlane/metadata/android/sv-SE/changelogs/40103070.txt
+++ b/fastlane/metadata/android/sv-SE/changelogs/40103070.txt
@@ -1,2 +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
+Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40103080.txt b/fastlane/metadata/android/sv-SE/changelogs/40103080.txt
new file mode 100644
index 0000000000..e466a7420a
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40103080.txt
@@ -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
diff --git a/fastlane/metadata/android/uk/changelogs/40103070.txt b/fastlane/metadata/android/uk/changelogs/40103070.txt
index ec17d4934b..9883169e67 100644
--- a/fastlane/metadata/android/uk/changelogs/40103070.txt
+++ b/fastlane/metadata/android/uk/changelogs/40103070.txt
@@ -1,2 +1,2 @@
Основні зміни в цій версії: виправлення помилок в основному у повідомленнях.
-Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.7
+Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/uk/changelogs/40103080.txt b/fastlane/metadata/android/uk/changelogs/40103080.txt
new file mode 100644
index 0000000000..3b5f491527
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/40103080.txt
@@ -0,0 +1,2 @@
+Основні зміни у цій версії: Виправлення помилок!
+Повний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.8
diff --git a/fastlane/metadata/android/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt
index 3c59d860ac..c046d8a40a 100644
--- a/fastlane/metadata/android/uk/full_description.txt
+++ b/fastlane/metadata/android/uk/full_description.txt
@@ -1,7 +1,7 @@
-Element — це і безпечний месенджер, і застосунок для співпраці команди, який ідеально підходить для групових бесід під час віддаленої роботи. Цей застосунок для спілкування застосовує наскрізне шифрування для забезпечення відеоконференцій, обміну файлами та голосових викликів.
+Element — це і безпечний месенджер, і застосунок для співпраці команди, який ідеально підходить спілкування групами під час віддаленої роботи. Цей застосунок для спілкування використовує наскрізне шифрування для забезпечення відеоконференцій, обміну файлами та голосових викликів.
Можливості Element включають:
-- Розширені засоби спілкування в Інтернеті
+- Розширені засоби онлайн-спілкування
- Повністю зашифровані повідомлення для надання можливості безпечнішого корпоративного спілкування, навіть для віддалених працівників
- Децентралізований чат на основі відкритого коду Matrix
- Безпечний обмін файлами із зашифрованими даними для керування проєктами
@@ -33,10 +33,10 @@ Element надає такі можливості на вибір:
Справжнє наскрізне шифрування (лише учасники бесіди можуть розшифровувати повідомлення) та взаємне підписування пристроїв.
Повноцінні спілкування та інтеграція
-Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та розширень. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання.
+Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та віджетів. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання.
Продовжуйте, де зупинилися
Залишайтеся на зв'язку, де б ви не знаходились, з повністю синхронізованою історією повідомлень на всіх своїх пристроях та в Інтернеті за адресою https://app.element.io
Відкритий код
-Element для Android це проєкт з відкритим кодом, розміщений GitHub. Будь ласка, повідомте про помилки та/або сприяйте його розвитку на https://github.com/vector-im/element-android
+Element для Android — це проєкт з відкритим кодом, розміщений на GitHub. Повідомляйте про помилки та/або допомагайте його розвитку на https://github.com/vector-im/element-android
diff --git a/fastlane/metadata/android/zh-CN/changelogs/40103070.txt b/fastlane/metadata/android/zh-CN/changelogs/40103070.txt
index f2fcf4df92..3f23d09b73 100644
--- a/fastlane/metadata/android/zh-CN/changelogs/40103070.txt
+++ b/fastlane/metadata/android/zh-CN/changelogs/40103070.txt
@@ -1,2 +1,2 @@
此版本的主要变化:主要关于通知的错误修复。
-完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.7
+完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/zh-CN/changelogs/40103080.txt b/fastlane/metadata/android/zh-CN/changelogs/40103080.txt
new file mode 100644
index 0000000000..873cb34746
--- /dev/null
+++ b/fastlane/metadata/android/zh-CN/changelogs/40103080.txt
@@ -0,0 +1,2 @@
+此版本主要变化:Bug 修复!
+完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.8
diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103070.txt b/fastlane/metadata/android/zh-TW/changelogs/40103070.txt
index 02b58cc956..1f9173fa1e 100644
--- a/fastlane/metadata/android/zh-TW/changelogs/40103070.txt
+++ b/fastlane/metadata/android/zh-TW/changelogs/40103070.txt
@@ -1,2 +1,2 @@
此版本中的主要變動:主要關於通知的臭蟲修復。
-完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.7
+完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2
diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103080.txt b/fastlane/metadata/android/zh-TW/changelogs/40103080.txt
new file mode 100644
index 0000000000..07689479a3
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/40103080.txt
@@ -0,0 +1,2 @@
+此版本中的主要變動:臭蟲修復!
+完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.8
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f5e3b23f79..fa58fc5aae 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=00b273629df4ce46e68df232161d5a7c4e495b9a029ce6e0420f071e21316867
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip
+distributionSha256Sum=dd54e87b4d7aa8ff3c6afb0f7805aa121d4b70bca55b8c9b1b896eb103184582
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml b/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml
index 4828810e34..c1ae9ef117 100644
--- a/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml
+++ b/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml
@@ -34,7 +34,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
+ app:layout_constraintTop_toBottomOf="@id/appBarLayout">
-
visible
-
diff --git a/library/ui-styles/src/main/res/transition/image_preview_transition.xml b/library/ui-styles/src/main/res/transition/image_preview_transition.xml
index 3674324c4e..c1af6d7973 100644
--- a/library/ui-styles/src/main/res/transition/image_preview_transition.xml
+++ b/library/ui-styles/src/main/res/transition/image_preview_transition.xml
@@ -11,5 +11,3 @@
-
-
diff --git a/library/ui-styles/src/main/res/values-sw600dp/tablet.xml b/library/ui-styles/src/main/res/values-sw600dp/tablet.xml
new file mode 100644
index 0000000000..39f467cf0d
--- /dev/null
+++ b/library/ui-styles/src/main/res/values-sw600dp/tablet.xml
@@ -0,0 +1,6 @@
+
+
+
+ 0.6
+
+
\ No newline at end of file
diff --git a/library/ui-styles/src/main/res/values-sw720dp/tablet.xml b/library/ui-styles/src/main/res/values-sw720dp/tablet.xml
new file mode 100644
index 0000000000..4afbe6c773
--- /dev/null
+++ b/library/ui-styles/src/main/res/values-sw720dp/tablet.xml
@@ -0,0 +1,6 @@
+
+
+
+ 0.5
+
+
\ No newline at end of file
diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml
index e2e50449ce..864f3d3d7f 100644
--- a/library/ui-styles/src/main/res/values/dimens.xml
+++ b/library/ui-styles/src/main/res/values/dimens.xml
@@ -32,7 +32,6 @@
88dp
8dp
-
76dp
@@ -41,4 +40,6 @@
320dp
+
+ 8dp
\ No newline at end of file
diff --git a/library/ui-styles/src/main/res/values/palette.xml b/library/ui-styles/src/main/res/values/palette.xml
index ed12f10af3..e37fd8a7c6 100644
--- a/library/ui-styles/src/main/res/values/palette.xml
+++ b/library/ui-styles/src/main/res/values/palette.xml
@@ -20,7 +20,6 @@
#5C56F5
#0086E6
-
#F4F6FA
#E3E8F0
diff --git a/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml b/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml
index c9be5175e0..88bee99f84 100644
--- a/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml
+++ b/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml
@@ -1,5 +1,4 @@
-
diff --git a/library/ui-styles/src/main/res/values/styles_social_login.xml b/library/ui-styles/src/main/res/values/styles_social_login.xml
index f601f36480..5a76f70f2e 100644
--- a/library/ui-styles/src/main/res/values/styles_social_login.xml
+++ b/library/ui-styles/src/main/res/values/styles_social_login.xml
@@ -49,7 +49,6 @@
- @android:color/black
-
@@ -68,7 +67,6 @@
- #3877EA
-
@@ -85,7 +83,6 @@
- #5D9EC9
-
@@ -118,5 +115,4 @@
- @android:color/black
-
\ No newline at end of file
diff --git a/library/ui-styles/src/main/res/values/tablet.xml b/library/ui-styles/src/main/res/values/tablet.xml
new file mode 100644
index 0000000000..a5df8fe17c
--- /dev/null
+++ b/library/ui-styles/src/main/res/values/tablet.xml
@@ -0,0 +1,6 @@
+
+
+
+ 1
+
+
\ No newline at end of file
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index eff972a271..f15746f73c 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -9,7 +9,7 @@ buildscript {
mavenCentral()
}
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.
testInstrumentationRunnerArguments clearPackageData: 'true'
- buildConfigField "String", "SDK_VERSION", "\"1.3.9\""
+ buildConfigField "String", "SDK_VERSION", "\"1.3.12\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
@@ -140,8 +140,8 @@ dependencies {
implementation libs.arrow.core
implementation libs.arrow.instances
- // olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
- implementation 'org.matrix.gitlab.matrix-org:olm:3.2.4'
+ // olm lib is now hosted by maven at https://gitlab.matrix.org/api/v4/projects/27/packages/maven
+ implementation 'org.matrix.android:olm:3.2.7'
// DI
implementation libs.dagger.dagger
@@ -158,10 +158,10 @@ dependencies {
implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber
- implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.38'
+ implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.39'
testImplementation libs.tests.junit
- testImplementation 'org.robolectric:robolectric:4.7.2'
+ testImplementation 'org.robolectric:robolectric:4.7.3'
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
testImplementation libs.mockk.mockk
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
index cf9b8f87c1..8e21828562 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt
@@ -20,9 +20,10 @@ import android.content.Context
import android.net.Uri
import androidx.lifecycle.Observer
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
-import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -30,7 +31,6 @@ import kotlinx.coroutines.withTimeout
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
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.MatrixConfiguration
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.TimelineSettings
import org.matrix.android.sdk.api.session.sync.SyncState
-import java.util.ArrayList
+import timber.log.Timber
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@@ -56,13 +56,14 @@ import java.util.concurrent.TimeUnit
*/
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 {
UiThreadStatement.runOnUiThread {
- Matrix.initialize(
+ TestMatrix.initialize(
context,
MatrixConfiguration(
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 {
@@ -95,33 +96,47 @@ class CommonTestHelper(context: Context) {
*
* @param session the session to sync
*/
- @Suppress("EXPERIMENTAL_API_USAGE")
- fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
+ fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) {
val lock = CountDownLatch(1)
-
- val job = GlobalScope.launch(Dispatchers.Main) {
- session.open()
- }
- runBlocking { job.join() }
-
- session.startSync(true)
-
- val syncLiveData = runBlocking(Dispatchers.Main) {
- session.getSyncStateLive()
- }
- val syncObserver = object : Observer {
- override fun onChanged(t: SyncState?) {
- if (session.hasAlreadySynced()) {
- lock.countDown()
- syncLiveData.removeObserver(this)
+ coroutineScope.launch {
+ session.startSync(true)
+ val syncLiveData = session.getSyncStateLive()
+ val syncObserver = object : Observer {
+ 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)
}
+ /**
+ * 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 {
+ 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
*
@@ -130,46 +145,57 @@ class CommonTestHelper(context: Context) {
* @param nbOfMessages the number of time the message will be sent
*/
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List {
- val timeline = room.createTimeline(null, TimelineSettings(10))
val sentEvents = ArrayList(nbOfMessages)
- val latch = CountDownLatch(1)
- val timelineListener = object : Timeline.Listener {
- override fun onTimelineFailure(throwable: Throwable) {
- }
+ val timeline = room.createTimeline(null, TimelineSettings(10))
+ timeline.start()
+ waitWithLatch(timeout + 1_000L * nbOfMessages) { latch ->
+ val timelineListener = object : Timeline.Listener {
+ override fun onTimelineFailure(throwable: Throwable) {
+ }
- override fun onNewTimelineEvents(eventIds: List) {
- // noop
- }
+ override fun onNewTimelineEvents(eventIds: List) {
+ // noop
+ }
- override fun onTimelineUpdated(snapshot: List) {
- val newMessages = snapshot
- .filter { it.root.sendState == SendState.SYNCED }
- .filter { it.root.getClearType() == EventType.MESSAGE }
- .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true }
+ override fun onTimelineUpdated(snapshot: List) {
+ val newMessages = snapshot
+ .filter { it.root.sendState == SendState.SYNCED }
+ .filter { it.root.getClearType() == EventType.MESSAGE }
+ .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true }
- if (newMessages.size == nbOfMessages) {
- sentEvents.addAll(newMessages)
- // Remove listener now, if not at the next update sendEvents could change
- timeline.removeListener(this)
- latch.countDown()
+ Timber.v("New synced message size: ${newMessages.size}")
+ if (newMessages.size == nbOfMessages) {
+ sentEvents.addAll(newMessages)
+ // Remove listener now, if not at the next update sendEvents could change
+ 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()
-
// Check that all events has been created
assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong())
-
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 *****************************************************************************
/**
@@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) {
assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session
+ session.open()
if (sessionTestParams.withInitialSync) {
syncSession(session, 60_000)
}
-
return session
}
@@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) {
.getLoginWizard()
.login(userName, password, "myDevice")
}
-
+ session.open()
if (sessionTestParams.withInitialSync) {
syncSession(session)
}
@@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) {
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
}
- @Suppress("EXPERIMENTAL_API_USAGE")
- fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
- GlobalScope.launch {
- while (true) {
- delay(1000)
- if (condition()) {
- latch.countDown()
- return@launch
- }
+ suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
+ while (true) {
+ delay(1000)
+ if (condition()) {
+ latch.countDown()
+ return
}
}
}
- 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)
- block(latch)
+ coroutineScope.launch(dispatcher) {
+ block(latch)
+ }
await(latch, timeout)
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
index a8cbc160dd..ccea6f53b9 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt
@@ -19,10 +19,6 @@ package org.matrix.android.sdk.common
import android.os.SystemClock
import android.util.Log
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.assertNotNull
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.UserPasswordAuth
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.crypto.verification.IncomingSasVerificationTransaction
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.create.CreateRoomParams
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_BACKUP
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import java.util.UUID
-import java.util.concurrent.CountDownLatch
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
-class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
+class CryptoTestHelper(private val testHelper: CommonTestHelper) {
private val messagesFromAlice: List = listOf("0 - Hello I'm Alice!", "4 - Go!")
private val messagesFromBob: List = 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
*/
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" })
}
-
if (encryptedRoom) {
- val room = aliceSession.getRoom(roomId)!!
-
- mTestHelper.runBlockingTest {
+ testHelper.waitWithLatch { latch ->
+ val room = aliceSession.getRoom(roomId)!!
room.enableEncryption()
+ val roomSummaryLive = room.getRoomSummaryLive()
+ val roomSummaryObserver = object : Observer> {
+ override fun onChanged(roomSummary: Optional) {
+ if (roomSummary.getOrNull()?.isEncrypted.orFalse()) {
+ roomSummaryLive.removeObserver(this)
+ latch.countDown()
+ }
+ }
+ }
+ roomSummaryLive.observeForever(roomSummaryObserver)
}
}
-
return CryptoTestData(roomId, listOf(aliceSession))
}
/**
* @return alice and bob sessions
*/
- @Suppress("EXPERIMENTAL_API_USAGE")
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
val aliceSession = cryptoTestData.firstSession
@@ -92,54 +95,37 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
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)
-
- val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
- bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
- }
-
- val newRoomObserver = object : Observer> {
- override fun onChanged(t: List?) {
- if (t?.isNotEmpty() == true) {
- lock1.countDown()
- bobRoomSummariesLive.removeObserver(this)
+ testHelper.waitWithLatch { latch ->
+ val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
+ val newRoomObserver = object : Observer> {
+ override fun onChanged(t: List?) {
+ if (t?.isNotEmpty() == true) {
+ bobRoomSummariesLive.removeObserver(this)
+ latch.countDown()
+ }
}
}
- }
-
- GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver)
- }
-
- mTestHelper.runBlockingTest {
aliceRoom.invite(bobSession.myUserId)
}
- mTestHelper.await(lock1)
-
- val lock = CountDownLatch(1)
-
- val roomJoinedObserver = object : Observer> {
- override fun onChanged(t: List?) {
- if (bobSession.getRoom(aliceRoomId)
- ?.getRoomMember(aliceSession.myUserId)
- ?.membership == Membership.JOIN) {
- lock.countDown()
- bobRoomSummariesLive.removeObserver(this)
+ testHelper.waitWithLatch { latch ->
+ val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
+ val roomJoinedObserver = object : Observer> {
+ override fun onChanged(t: List?) {
+ if (bobSession.getRoom(aliceRoomId)
+ ?.getRoomMember(bobSession.myUserId)
+ ?.membership == Membership.JOIN) {
+ bobRoomSummariesLive.removeObserver(this)
+ latch.countDown()
+ }
}
}
- }
-
- GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(roomJoinedObserver)
+ bobSession.joinRoom(aliceRoomId)
}
-
- mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) }
-
- mTestHelper.await(lock)
-
// Ensure bob can send messages to the room
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
// assertNotNull(roomFromBobPOV.powerLevels)
@@ -171,13 +157,13 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
* @Return Sam 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)
}
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
samSession.joinRoom(room.roomId, null, emptyList())
}
@@ -194,23 +180,20 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
val bobSession = cryptoTestData.secondSession!!
bobSession.cryptoService().setWarnOnUnknownDevices(false)
-
aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
// Alice sends a message
- mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1)
-// roomFromAlicePOV.sendTextMessage(messagesFromAlice[0])
+ testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1)
// Bob send 3 messages
- mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1)
- mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1)
- mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1)
+ testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1)
+ testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1)
+ testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1)
// Alice sends a message
- mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1)
-
+ testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1)
return cryptoTestData
}
@@ -256,60 +239,44 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
)
}
- @Suppress("EXPERIMENTAL_API_USAGE")
fun createDM(alice: Session, bob: Session): String {
- val roomId = mTestHelper.runBlockingTest {
- alice.createDirectRoom(bob.myUserId)
- }
-
- mTestHelper.waitWithLatch { latch ->
- val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
- bob.getRoomSummariesLive(roomSummaryQueryParams { })
- }
-
+ var roomId: String = ""
+ testHelper.waitWithLatch { latch ->
+ roomId = alice.createDirectRoom(bob.myUserId)
+ val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
val newRoomObserver = object : Observer> {
override fun onChanged(t: List?) {
val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1
if (indexOfFirst != -1) {
- latch.countDown()
bobRoomSummariesLive.removeObserver(this)
+ latch.countDown()
}
}
}
-
- GlobalScope.launch(Dispatchers.Main) {
- bobRoomSummariesLive.observeForever(newRoomObserver)
- }
+ bobRoomSummariesLive.observeForever(newRoomObserver)
}
- mTestHelper.waitWithLatch { latch ->
- val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
- bob.getRoomSummariesLive(roomSummaryQueryParams { })
- }
-
+ testHelper.waitWithLatch { latch ->
+ val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
val newRoomObserver = object : Observer> {
override fun onChanged(t: List?) {
if (bob.getRoom(roomId)
?.getRoomMember(bob.myUserId)
?.membership == Membership.JOIN) {
- latch.countDown()
bobRoomSummariesLive.removeObserver(this)
+ latch.countDown()
}
}
}
-
- GlobalScope.launch(Dispatchers.Main) {
- bobRoomSummariesLive.observeForever(newRoomObserver)
- }
-
- mTestHelper.runBlockingTest { bob.joinRoom(roomId) }
+ bobRoomSummariesLive.observeForever(newRoomObserver)
+ bob.joinRoom(roomId)
}
return roomId
}
fun initializeCrossSigning(session: Session) {
- mTestHelper.doSync {
+ testHelper.doSync {
session.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -346,8 +313,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
var bobPovTx: IncomingSasVerificationTransaction? = null
// wait for alice to get the ready
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
if (bobPovTx?.state == VerificationTxState.OnStarted) {
@@ -359,16 +326,16 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
}
}
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction
Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}")
alicePovTx?.state == VerificationTxState.ShortCodeReady
}
}
// wait for alice to get the ready
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
if (bobPovTx?.state == VerificationTxState.OnStarted) {
@@ -383,38 +350,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
bobPovTx!!.userHasVerifiedShortCode()
alicePovTx!!.userHasVerifiedShortCode()
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
}
}
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
}
}
}
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)
- val roomId = mTestHelper.runBlockingTest {
+ val roomId = testHelper.runBlockingTest {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
}
val room = aliceSession.getRoom(roomId)!!
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
room.enableEncryption()
}
val sessions = mutableListOf(aliceSession)
for (index in 1 until numberOfMembers) {
- val session = mTestHelper.createAccount("User_$index", defaultSessionParams)
- mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
+ val session = testHelper.createAccount("User_$index", defaultSessionParams)
+ testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
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")
sessions.add(session)
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt
new file mode 100644
index 0000000000..c1596c281c
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt
@@ -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
+}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt
similarity index 80%
rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt
rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt
index 8b9b6efa11..e92232a7c5 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt
@@ -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");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.matrix.android.sdk.api
+package org.matrix.android.sdk.common
import android.content.Context
import android.os.Handler
@@ -24,27 +24,27 @@ import androidx.work.Configuration
import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy
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.HomeServerHistoryService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.network.ApiInterceptorListener
import org.matrix.android.sdk.api.network.ApiPath
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.network.ApiInterceptor
import org.matrix.android.sdk.internal.network.UserAgentHolder
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
+import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
/**
- * This is the main entry point to the matrix sdk.
- * To get the singleton instance, use getInstance static method.
+ * This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent.
*/
-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 authenticationService: AuthenticationService
@@ -55,15 +55,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var sessionManager: SessionManager
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
@Inject internal lateinit var apiInterceptor: ApiInterceptor
+ @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
private val uiHandler = Handler(Looper.getMainLooper())
init {
Monarchy.init(context)
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
- if (context.applicationContext !is Configuration.Provider) {
- WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
- }
+ val configuration = Configuration.Builder()
+ .setExecutor(Executors.newCachedThreadPool())
+ .setWorkerFactory(matrixWorkerFactory)
+ .build()
+ WorkManager.initialize(context, configuration)
uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
@@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
companion object {
- private lateinit var instance: Matrix
+ private lateinit var instance: TestMatrix
private val isInit = AtomicBoolean(false)
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
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)) {
val appContext = context.applicationContext
if (appContext is MatrixConfiguration.Provider) {
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
- instance = Matrix(appContext, matrixConfiguration)
+ instance = TestMatrix(appContext, matrixConfiguration)
} else {
throw IllegalStateException("Matrix is not initialized properly." +
" You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt
index 1d05e655af..d0f0e23152 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt
@@ -34,12 +34,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
NetworkModule::class,
AuthModule::class,
RawModule::class,
- SystemModule::class,
- TestNetworkModule::class
+ SystemModule::class
])
@MatrixScope
internal interface TestMatrixComponent : MatrixComponent {
+ fun inject(matrix: TestMatrix)
+
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context,
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt
index 3e4d5fe08e..d82bac3700 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt
@@ -18,10 +18,39 @@ package org.matrix.android.sdk.common
import dagger.Binds
import dagger.Module
+import dagger.Provides
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
internal abstract class TestModule {
@Binds
abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent
+
+ @Module
+ companion object {
+
+ val interceptors = ArrayList()
+
+ 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()
+ }
+ }
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt
index 825fba570a..d0f63227f5 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt
@@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
@FixMethodOrder(MethodSorters.JVM)
class PreShareKeysTest : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
fun ensure_outbound_session_happy_path() {
- val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
+ val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
val e2eRoomID = testData.roomId
val aliceSession = testData.firstSession
val bobSession = testData.secondSession!!
@@ -58,12 +58,12 @@ class PreShareKeysTest : InstrumentedTest {
Log.d("#Test", "Room Key Received from alice $preShareCount")
// Force presharing of new outbound key
- mTestHelper.doSync {
+ testHelper.doSync {
aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it)
}
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
it.senderId == aliceSession.myUserId &&
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)
// 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()?.sessionId, "Unexpected megolm session")
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
}
}
- mTestHelper.signOutAndClose(aliceSession)
- mTestHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(bobSession)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt
index cf31294e2f..458eae6ab2 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt
@@ -62,8 +62,8 @@ import kotlin.coroutines.resume
class UnwedgingTest : InstrumentedTest {
private lateinit var messagesReceivedByBob: List
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Before
fun init() {
@@ -85,7 +85,7 @@ class UnwedgingTest : InstrumentedTest {
*/
@Test
fun testUnwedging() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
@@ -133,7 +133,7 @@ class UnwedgingTest : InstrumentedTest {
roomFromAlicePOV.sendTextMessage("First message")
// Wait for the message to be received by Bob
- mTestHelper.await(latch)
+ testHelper.await(latch)
bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 1
@@ -161,7 +161,7 @@ class UnwedgingTest : InstrumentedTest {
roomFromAlicePOV.sendTextMessage("Second message")
// Wait for the message to be received by Bob
- mTestHelper.await(latch)
+ testHelper.await(latch)
bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 2
@@ -179,7 +179,7 @@ class UnwedgingTest : InstrumentedTest {
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
// Wait for the message to be received by Bob
- mTestHelper.waitWithLatch {
+ testHelper.waitWithLatch {
bobEventsListener = createEventListener(it, 3)
bobTimeline.addListener(bobEventsListener)
messagesReceivedByBob = emptyList()
@@ -201,11 +201,11 @@ class UnwedgingTest : InstrumentedTest {
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].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
- mTestHelper.await(bobFinalLatch)
+ testHelper.await(bobFinalLatch)
bobTimeline.removeListener(bobHasThreeDecryptedEventsListener)
// It's a trick to force key request on fail to decrypt
- mTestHelper.doSync {
+ testHelper.doSync {
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -222,8 +222,8 @@ class UnwedgingTest : InstrumentedTest {
}
// Wait until we received back the key
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
// we should get back the key and be able to decrypt
val result = tryOrNull {
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
@@ -235,7 +235,7 @@ class UnwedgingTest : InstrumentedTest {
bobTimeline.dispose()
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
index 44af87bcbe..d9cc7a8ac0 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt
@@ -45,14 +45,14 @@ import kotlin.coroutines.resume
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class XSigningTest : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
fun test_InitializeAndStoreKeys() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
- mTestHelper.doSync {
+ testHelper.doSync {
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
@@ -79,12 +79,12 @@ class XSigningTest : InstrumentedTest {
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
- mTestHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(aliceSession)
}
@Test
fun test_CrossSigningCheckBobSeesTheKeys() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -98,21 +98,21 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD
)
- mTestHelper.doSync {
+ testHelper.doSync {
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
promise.resume(aliceAuthParams)
}
}, it)
}
- mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
promise.resume(bobAuthParams)
}
}, it) }
// Check that alice can see bob keys
- mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
+ testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
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())
- mTestHelper.signOutAndClose(aliceSession)
- mTestHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(bobSession)
}
@Test
fun test_CrossSigningTestAliceTrustBobNewDevice() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -144,12 +144,12 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD
)
- mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ testHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
promise.resume(aliceAuthParams)
}
}, it) }
- mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
+ testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) {
promise.resume(bobAuthParams)
}
@@ -157,21 +157,21 @@ class XSigningTest : InstrumentedTest {
// Check that alice can see bob keys
val bobUserId = bobSession.myUserId
- mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
+ testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
- mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) }
+ testHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, 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
- val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true))
+ val bobSession2 = testHelper.logIntoAccount(bobUserId, SessionTestParams(true))
val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
// Check that bob first session sees the new login
- val data = mTestHelper.doSync> {
+ val data = testHelper.doSync> {
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)
// Manually mark it as trusted from first session
- mTestHelper.doSync {
+ testHelper.doSync {
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
}
// Now alice should cross trust bob's second device
- val data2 = mTestHelper.doSync> {
+ val data2 = testHelper.doSync> {
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
}
@@ -200,8 +200,8 @@ class XSigningTest : InstrumentedTest {
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
- mTestHelper.signOutAndClose(aliceSession)
- mTestHelper.signOutAndClose(bobSession)
- mTestHelper.signOutAndClose(bobSession2)
+ testHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(bobSession2)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt
index da5e90abdd..189fc405eb 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt
@@ -40,8 +40,9 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EncryptionTest : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
fun test_EncryptionEvent() {
@@ -69,7 +70,7 @@ class EncryptionTest : InstrumentedTest {
}
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
val aliceSession = cryptoTestData.firstSession
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
@@ -101,12 +102,12 @@ class EncryptionTest : InstrumentedTest {
timeline.addListener(timelineListener)
action.invoke(room)
-
- mTestHelper.await(latch)
+ testHelper.await(latch)
timeline.dispose()
-
- room.isEncrypted() shouldBe roomShouldBeEncrypted
-
- cryptoTestData.cleanUp(mTestHelper)
+ testHelper.waitWithLatch {
+ room.isEncrypted() shouldBe roomShouldBeEncrypted
+ it.countDown()
+ }
+ cryptoTestData.cleanUp(testHelper)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt
index 40659cef11..975d481628 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt
@@ -44,7 +44,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.common.CommonTestHelper
-import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.common.SessionTestParams
import org.matrix.android.sdk.common.TestConstants
import org.matrix.android.sdk.internal.crypto.GossipingRequestState
@@ -55,7 +54,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
-import java.util.concurrent.CountDownLatch
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
@@ -63,15 +61,14 @@ import kotlin.coroutines.resume
@FixMethodOrder(MethodSorters.JVM)
class KeyShareTests : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val commonTestHelper = CommonTestHelper(context())
@Test
fun test_DoNotSelfShareIfNotTrusted() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
// Create an encrypted room and add a message
- val roomId = mTestHelper.runBlockingTest {
+ val roomId = commonTestHelper.runBlockingTest {
aliceSession.createRoom(
CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE
@@ -83,11 +80,11 @@ class KeyShareTests : InstrumentedTest {
assertNotNull(room)
Thread.sleep(4_000)
assertTrue(room?.isEncrypted() == true)
- val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
+ val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
// Open a new sessionx
- val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
+ val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
@@ -105,25 +102,24 @@ class KeyShareTests : InstrumentedTest {
// Try to request
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
- val waitLatch = CountDownLatch(1)
val eventMegolmSessionId = receivedEvent.root.content.toModel()?.sessionId
var outGoingRequestId: String? = null
- mTestHelper.retryPeriodicallyWithLatch(waitLatch) {
- aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
- .filter { req ->
- // filter out request that was known before
- !outgoingRequestsBefore.any { req.requestId == it.requestId }
- }
- .let {
- val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
- outGoingRequestId = outgoing?.requestId
- outgoing != null
- }
+ commonTestHelper.waitWithLatch { latch ->
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
+ aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
+ .filter { req ->
+ // filter out request that was known before
+ !outgoingRequestsBefore.any { req.requestId == it.requestId }
+ }
+ .let {
+ val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
+ outGoingRequestId = outgoing?.requestId
+ outgoing != null
+ }
+ }
}
- mTestHelper.await(waitLatch)
-
Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
@@ -134,8 +130,8 @@ class KeyShareTests : InstrumentedTest {
// The first session should see an incoming request
// the request should be refused, because the device is not trusted
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ commonTestHelper.waitWithLatch { latch ->
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
// DEBUG LOGS
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
@@ -164,8 +160,8 @@ class KeyShareTests : InstrumentedTest {
// Re request
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ commonTestHelper.waitWithLatch { latch ->
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
Log.v("TEST", "Incoming request Session 1")
Log.v("TEST", "=========================")
@@ -180,8 +176,8 @@ class KeyShareTests : InstrumentedTest {
}
Thread.sleep(6_000)
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ commonTestHelper.waitWithLatch { latch ->
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let {
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
}
@@ -194,15 +190,15 @@ class KeyShareTests : InstrumentedTest {
fail("should have been able to decrypt")
}
- mTestHelper.signOutAndClose(aliceSession)
- mTestHelper.signOutAndClose(aliceSession2)
+ commonTestHelper.signOutAndClose(aliceSession)
+ commonTestHelper.signOutAndClose(aliceSession2)
}
@Test
fun test_ShareSSSSSecret() {
- val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
- mTestHelper.doSync {
+ commonTestHelper.doSync {
aliceSession1.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -218,25 +214,25 @@ class KeyShareTests : InstrumentedTest {
}
// Also bootstrap keybackup on first session
- val creationInfo = mTestHelper.doSync {
+ val creationInfo = commonTestHelper.doSync {
aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
}
- val version = mTestHelper.doSync {
+ val version = commonTestHelper.doSync {
aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
}
// Save it for gossiping
aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
- val aliceSession2 = mTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true))
+ val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true))
val aliceVerificationService1 = aliceSession1.cryptoService().verificationService()
val aliceVerificationService2 = aliceSession2.cryptoService().verificationService()
// force keys download
- mTestHelper.doSync> {
+ commonTestHelper.doSync> {
aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it)
}
- mTestHelper.doSync> {
+ commonTestHelper.doSync> {
aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it)
}
@@ -276,8 +272,8 @@ class KeyShareTests : InstrumentedTest {
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
?: "", txId)
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ commonTestHelper.waitWithLatch { latch ->
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true
}
}
@@ -290,31 +286,31 @@ class KeyShareTests : InstrumentedTest {
// SSK and USK private keys should have been shared
- mTestHelper.waitWithLatch(60_000) { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ commonTestHelper.waitWithLatch(60_000) { latch ->
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
aliceSession2.cryptoService().crossSigningService().canCrossSign()
}
}
// Test that key backup key has been shared to
- mTestHelper.waitWithLatch(60_000) { latch ->
+ commonTestHelper.waitWithLatch(60_000) { latch ->
val keysBackupService = aliceSession2.cryptoService().keysBackupService()
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ commonTestHelper.retryPeriodicallyWithLatch(latch) {
Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}")
keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey
}
}
- mTestHelper.signOutAndClose(aliceSession1)
- mTestHelper.signOutAndClose(aliceSession2)
+ commonTestHelper.signOutAndClose(aliceSession1)
+ commonTestHelper.signOutAndClose(aliceSession2)
}
@Test
fun test_ImproperKeyShareBug() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
- mTestHelper.doSync {
+ commonTestHelper.doSync {
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -331,7 +327,7 @@ class KeyShareTests : InstrumentedTest {
}
// Create an encrypted room and send a couple of messages
- val roomId = mTestHelper.runBlockingTest {
+ val roomId = commonTestHelper.runBlockingTest {
aliceSession.createRoom(
CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE
@@ -343,12 +339,12 @@ class KeyShareTests : InstrumentedTest {
assertNotNull(roomAlicePov)
Thread.sleep(1_000)
assertTrue(roomAlicePov?.isEncrypted() == true)
- val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
+ val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
// Create bob session
- val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
- mTestHelper.doSync {
+ val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
+ commonTestHelper.doSync {
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -365,11 +361,11 @@ class KeyShareTests : InstrumentedTest {
}
// Let alice invite bob
- mTestHelper.runBlockingTest {
+ commonTestHelper.runBlockingTest {
roomAlicePov.invite(bobSession.myUserId, null)
}
- mTestHelper.runBlockingTest {
+ commonTestHelper.runBlockingTest {
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList())
}
@@ -377,7 +373,7 @@ class KeyShareTests : InstrumentedTest {
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
// and now resend a new message to reset index to 0
- mTestHelper.sendTextMessage(roomAlicePov, "After", 1)
+ commonTestHelper.sendTextMessage(roomAlicePov, "After", 1)
val roomRoomBobPov = aliceSession.getRoom(roomId)
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt
index c939952dc9..c835c2d40b 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt
@@ -41,8 +41,8 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
@FixMethodOrder(MethodSorters.JVM)
class WithHeldTests : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
fun test_WithHeldUnverifiedReason() {
@@ -50,19 +50,19 @@ class WithHeldTests : InstrumentedTest {
// ARRANGE
// =============================
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
- val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val bobSession = testHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
// Initialize cross signing on both
- mCryptoTestHelper.initializeCrossSigning(aliceSession)
- mCryptoTestHelper.initializeCrossSigning(bobSession)
+ cryptoTestHelper.initializeCrossSigning(aliceSession)
+ cryptoTestHelper.initializeCrossSigning(bobSession)
- val roomId = mCryptoTestHelper.createDM(aliceSession, bobSession)
- mCryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId)
+ val roomId = cryptoTestHelper.createDM(aliceSession, bobSession)
+ cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId)
val roomAlicePOV = aliceSession.getRoom(roomId)!!
- val bobUnverifiedSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
+ val bobUnverifiedSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
// =============================
// ACT
@@ -71,11 +71,11 @@ class WithHeldTests : InstrumentedTest {
// Alice decide to not send to unverified sessions
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true)
- val timelineEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first()
+ val timelineEvent = testHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first()
// await for bob unverified session to get the message
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null
}
}
@@ -101,10 +101,10 @@ class WithHeldTests : InstrumentedTest {
// enable back sending to unverified
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false)
- val secondEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first()
+ val secondEvent = testHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first()
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId)
// wait until it's decrypted
ev?.root?.getClearType() == EventType.MESSAGE
@@ -123,17 +123,17 @@ class WithHeldTests : InstrumentedTest {
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
}
- mTestHelper.signOutAndClose(aliceSession)
- mTestHelper.signOutAndClose(bobSession)
- mTestHelper.signOutAndClose(bobUnverifiedSession)
+ testHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(bobUnverifiedSession)
}
@Test
fun test_WithHeldNoOlm() {
- val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession
val bobSession = testData.secondSession!!
- val aliceInterceptor = mTestHelper.getTestInterceptor(aliceSession)
+ val aliceInterceptor = testHelper.getTestInterceptor(aliceSession)
// Simulate no OTK
aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule(
@@ -147,11 +147,11 @@ class WithHeldTests : InstrumentedTest {
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
- val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
+ val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
// await for bob session to get the message
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null
}
}
@@ -177,14 +177,14 @@ class WithHeldTests : InstrumentedTest {
// Add a new device for bob
aliceInterceptor.clearRules()
- val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true))
+ val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true))
// send a second message
- val secondMessageId = mTestHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId
+ val secondMessageId = testHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId
// Check that the
// await for bob SecondSession session to get the message
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null
}
}
@@ -194,27 +194,27 @@ class WithHeldTests : InstrumentedTest {
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
aliceInterceptor.clearRules()
- testData.cleanUp(mTestHelper)
- mTestHelper.signOutAndClose(bobSecondSession)
+ testData.cleanUp(testHelper)
+ testHelper.signOutAndClose(bobSecondSession)
}
@Test
fun test_WithHeldKeyRequest() {
- val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession
val bobSession = testData.secondSession!!
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
- val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
+ val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
- mTestHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(bobSession)
// Create a new session for bob
- val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
+ val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
// initialize to force request keys if missing
- mCryptoTestHelper.initializeCrossSigning(bobSecondSession)
+ cryptoTestHelper.initializeCrossSigning(bobSecondSession)
// Trust bob second device from Alice POV
aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback())
@@ -223,8 +223,8 @@ class WithHeldTests : InstrumentedTest {
var sessionId: String? = null
// Check that the
// await for bob SecondSession session to get the message
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also {
// try to decrypt and force key request
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
@@ -235,8 +235,8 @@ class WithHeldTests : InstrumentedTest {
}
// Check that bob second session requested the key
- mTestHelper.waitWithLatch { latch ->
- mTestHelper.retryPeriodicallyWithLatch(latch) {
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!)
wc?.code == WithHeldCode.UNAUTHORISED
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt
index 0785dba8b9..2a07b74115 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt
@@ -22,7 +22,6 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,7 +42,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreat
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
-import java.util.ArrayList
import java.util.Collections
import java.util.concurrent.CountDownLatch
@@ -51,9 +49,9 @@ import java.util.concurrent.CountDownLatch
@FixMethodOrder(MethodSorters.JVM)
class KeysBackupTest : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
- private val mKeysBackupTestHelper = KeysBackupTestHelper(mTestHelper, mCryptoTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
+ private val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
/**
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
@@ -62,7 +60,7 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun roomKeysTest_testBackupStore_ok() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
// From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
@@ -92,7 +90,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -100,7 +98,7 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun prepareKeysBackupVersionTest() {
- val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
+ val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
assertNotNull(bobSession.cryptoService().keysBackupService())
@@ -110,7 +108,7 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled)
- val megolmBackupCreationInfo = mTestHelper.doSync {
+ val megolmBackupCreationInfo = testHelper.doSync {
keysBackup.prepareKeysBackupVersion(null, null, it)
}
@@ -120,7 +118,7 @@ class KeysBackupTest : InstrumentedTest {
assertNotNull(megolmBackupCreationInfo.recoveryKey)
stateObserver.stopAndCheckStates(null)
- mTestHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(bobSession)
}
/**
@@ -128,7 +126,7 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun createKeysBackupVersionTest() {
- val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
+ val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
val keysBackup = bobSession.cryptoService().keysBackupService()
@@ -136,14 +134,14 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled)
- val megolmBackupCreationInfo = mTestHelper.doSync {
+ val megolmBackupCreationInfo = testHelper.doSync {
keysBackup.prepareKeysBackupVersion(null, null, it)
}
assertFalse(keysBackup.isEnabled)
// Create the version
- mTestHelper.doSync {
+ testHelper.doSync {
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
}
@@ -151,7 +149,7 @@ class KeysBackupTest : InstrumentedTest {
assertTrue(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null)
- mTestHelper.signOutAndClose(bobSession)
+ testHelper.signOutAndClose(bobSession)
}
/**
@@ -160,8 +158,9 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun backupAfterCreateKeysBackupVersionTest() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ keysBackupTestHelper.waitForKeybackUpBatching()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val latch = CountDownLatch(1)
@@ -171,9 +170,9 @@ class KeysBackupTest : InstrumentedTest {
val stateObserver = StateObserver(keysBackup, latch, 5)
- mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
- mTestHelper.await(latch)
+ testHelper.await(latch)
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)
@@ -191,7 +190,7 @@ class KeysBackupTest : InstrumentedTest {
KeysBackupState.ReadyToBackUp
)
)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -199,13 +198,13 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun backupAllGroupSessionsTest() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup)
- mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Check that backupAllGroupSessions returns valid data
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
@@ -214,7 +213,7 @@ class KeysBackupTest : InstrumentedTest {
var lastBackedUpKeysProgress = 0
- mTestHelper.doSync {
+ testHelper.doSync {
keysBackup.backupAllGroupSessions(object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
assertEquals(nbOfKeys, total)
@@ -230,7 +229,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
stateObserver.stopAndCheckStates(null)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -243,7 +242,7 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun testEncryptAndDecryptKeysBackupData() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService
@@ -252,7 +251,7 @@ class KeysBackupTest : InstrumentedTest {
// - Pick a megolm key
val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0]
- val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo
+ val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo
// - Check encryptGroupSession() returns stg
val keyBackupData = keysBackup.encryptGroupSession(session)
@@ -270,10 +269,10 @@ class KeysBackupTest : InstrumentedTest {
decryption!!)
assertNotNull(sessionData)
// - Compare the decrypted megolm key with the original one
- mKeysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
+ keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
stateObserver.stopAndCheckStates(null)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -284,10 +283,10 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun restoreKeysBackupTest() {
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Restore the e2e backup from the homeserver
- val importRoomKeysResult = mTestHelper.doSync {
+ val importRoomKeysResult = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
null,
@@ -297,9 +296,9 @@ class KeysBackupTest : InstrumentedTest {
)
}
- mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
+ keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -369,7 +368,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionTest() {
// - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -379,7 +378,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
// - Trust the backup from the new device
- mTestHelper.doSync {
+ testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
true,
@@ -388,21 +387,21 @@ class KeysBackupTest : InstrumentedTest {
}
// Wait for backup state to be ReadyToBackUp
- mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
+ keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
// - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
// - Retrieve the last version from the server
- val keysVersionResult = mTestHelper.doSync {
+ val keysVersionResult = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
}
// - It must be the same
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
- val keysBackupVersionTrust = mTestHelper.doSync {
+ val keysBackupVersionTrust = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
}
@@ -411,7 +410,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(2, keysBackupVersionTrust.signatures.size)
stateObserver.stopAndCheckStates(null)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -428,7 +427,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionWithRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -438,7 +437,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
// - Trust the backup from the new device with the recovery key
- mTestHelper.doSync {
+ testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
@@ -447,21 +446,21 @@ class KeysBackupTest : InstrumentedTest {
}
// Wait for backup state to be ReadyToBackUp
- mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
+ keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
// - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
// - Retrieve the last version from the server
- val keysVersionResult = mTestHelper.doSync {
+ val keysVersionResult = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
}
// - It must be the same
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
- val keysBackupVersionTrust = mTestHelper.doSync {
+ val keysBackupVersionTrust = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
}
@@ -470,7 +469,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(2, keysBackupVersionTrust.signatures.size)
stateObserver.stopAndCheckStates(null)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -485,7 +484,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -501,7 +500,7 @@ class KeysBackupTest : InstrumentedTest {
"Bad recovery key",
TestMatrixCallback(latch, false)
)
- mTestHelper.await(latch)
+ testHelper.await(latch)
// - The new device must still see the previous backup as not trusted
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
@@ -509,7 +508,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
stateObserver.stopAndCheckStates(null)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -528,7 +527,7 @@ class KeysBackupTest : InstrumentedTest {
// - Do an e2e backup to the homeserver with a password
// - And log Alice on a new device
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -538,7 +537,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
// - Trust the backup from the new device with the password
- mTestHelper.doSync {
+ testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
password,
@@ -547,21 +546,21 @@ class KeysBackupTest : InstrumentedTest {
}
// Wait for backup state to be ReadyToBackUp
- mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
+ keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
// - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
// - Retrieve the last version from the server
- val keysVersionResult = mTestHelper.doSync {
+ val keysVersionResult = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
}
// - It must be the same
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
- val keysBackupVersionTrust = mTestHelper.doSync {
+ val keysBackupVersionTrust = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
}
@@ -570,7 +569,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(2, keysBackupVersionTrust.signatures.size)
stateObserver.stopAndCheckStates(null)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -588,7 +587,7 @@ class KeysBackupTest : InstrumentedTest {
// - Do an e2e backup to the homeserver with a password
// - And log Alice on a new device
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@@ -604,7 +603,7 @@ class KeysBackupTest : InstrumentedTest {
badPassword,
TestMatrixCallback(latch, false)
)
- mTestHelper.await(latch)
+ testHelper.await(latch)
// - The new device must still see the previous backup as not trusted
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
@@ -612,7 +611,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
stateObserver.stopAndCheckStates(null)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -623,7 +622,7 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Try to restore the e2e backup with a wrong recovery key
val latch2 = CountDownLatch(1)
@@ -640,12 +639,12 @@ class KeysBackupTest : InstrumentedTest {
}
}
)
- mTestHelper.await(latch2)
+ testHelper.await(latch2)
// onSuccess may not have been called
assertNull(importRoomKeysResult)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -658,12 +657,12 @@ class KeysBackupTest : InstrumentedTest {
fun testBackupWithPassword() {
val password = "password"
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Restore the e2e backup with the password
val steps = ArrayList()
- val importRoomKeysResult = mTestHelper.doSync {
+ val importRoomKeysResult = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
password,
null,
@@ -698,9 +697,9 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress)
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
- mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
+ keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -714,7 +713,7 @@ class KeysBackupTest : InstrumentedTest {
val password = "password"
val wrongPassword = "passw0rd"
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Try to restore the e2e backup with a wrong password
val latch2 = CountDownLatch(1)
@@ -731,12 +730,12 @@ class KeysBackupTest : InstrumentedTest {
}
}
)
- mTestHelper.await(latch2)
+ testHelper.await(latch2)
// onSuccess may not have been called
assertNull(importRoomKeysResult)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -749,10 +748,10 @@ class KeysBackupTest : InstrumentedTest {
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
val password = "password"
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Restore the e2e backup with the recovery key.
- val importRoomKeysResult = mTestHelper.doSync {
+ val importRoomKeysResult = testHelper.doSync {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
null,
@@ -762,9 +761,9 @@ class KeysBackupTest : InstrumentedTest {
)
}
- mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
+ keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -775,7 +774,7 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
- val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
+ val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Try to restore the e2e backup with a password
val latch2 = CountDownLatch(1)
@@ -792,12 +791,12 @@ class KeysBackupTest : InstrumentedTest {
}
}
)
- mTestHelper.await(latch2)
+ testHelper.await(latch2)
// onSuccess may not have been called
assertNull(importRoomKeysResult)
- testData.cleanUp(mTestHelper)
+ testData.cleanUp(testHelper)
}
/**
@@ -807,22 +806,22 @@ class KeysBackupTest : InstrumentedTest {
@Test
fun testIsKeysBackupTrusted() {
// - Create a backup version
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup)
// - Do an e2e backup to the homeserver
- mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Get key backup version from the homeserver
- val keysVersionResult = mTestHelper.doSync {
+ val keysVersionResult = testHelper.doSync {
keysBackup.getCurrentVersion(it)
}
// - Check the returned KeyBackupVersion is trusted
- val keysBackupVersionTrust = mTestHelper.doSync {
+ val keysBackupVersionTrust = testHelper.doSync {
keysBackup.getKeysBackupTrust(keysVersionResult!!, it)
}
@@ -837,7 +836,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
stateObserver.stopAndCheckStates(null)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -849,9 +848,8 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() {
- fail("This test still fail. To investigate")
// - Create a backup version
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@@ -859,15 +857,15 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled)
- val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled)
// - Restart alice session
// - Log Alice on a new device
- val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
+ val aliceSession2 = testHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
@@ -891,13 +889,13 @@ class KeysBackupTest : InstrumentedTest {
}
}
})
- mTestHelper.await(latch)
+ testHelper.await(latch)
assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion)
stateObserver.stopAndCheckStates(null)
stateObserver2.stopAndCheckStates(null)
- mTestHelper.signOutAndClose(aliceSession2)
+ testHelper.signOutAndClose(aliceSession2)
}
/**
@@ -911,7 +909,7 @@ class KeysBackupTest : InstrumentedTest {
@Test
fun testBackupWhenAnotherBackupWasCreated() {
// - Create a backup version
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@@ -939,15 +937,15 @@ class KeysBackupTest : InstrumentedTest {
})
// - Make alice back up her keys to her homeserver
- mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled)
- mTestHelper.await(latch0)
+ testHelper.await(latch0)
// - Create a new backup with fake data on the homeserver, directly using the rest client
- val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo()
- mTestHelper.doSync {
+ val megolmBackupCreationInfo = cryptoTestHelper.createFakeMegolmBackupCreationInfo()
+ testHelper.doSync {
(keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it)
}
@@ -957,14 +955,14 @@ class KeysBackupTest : InstrumentedTest {
// - Make alice back up all her keys again
val latch2 = CountDownLatch(1)
keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false))
- mTestHelper.await(latch2)
+ testHelper.await(latch2)
// -> That must fail and her backup state must be WrongBackUpVersion
assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state)
assertFalse(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -982,17 +980,17 @@ class KeysBackupTest : InstrumentedTest {
@Test
fun testBackupAfterVerifyingADevice() {
// - Create a backup version
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup)
// - Make alice back up her keys to her homeserver
- mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Wait for keys backup to finish by asking again to backup keys.
- mTestHelper.doSync {
+ testHelper.doSync {
keysBackup.backupAllGroupSessions(null, it)
}
@@ -1001,14 +999,14 @@ class KeysBackupTest : InstrumentedTest {
val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device
- val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
+ val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
// - Post a message to have a new megolm session
aliceSession2.cryptoService().setWarnOnUnknownDevices(false)
val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!!
- mTestHelper.sendTextMessage(room2, "New key", 1)
+ testHelper.sendTextMessage(room2, "New key", 1)
// - Try to backup all in aliceSession2, it must fail
val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
@@ -1025,7 +1023,7 @@ class KeysBackupTest : InstrumentedTest {
super.onSuccess(data)
}
})
- mTestHelper.await(latch2)
+ testHelper.await(latch2)
assertFalse(isSuccessful)
@@ -1049,12 +1047,12 @@ class KeysBackupTest : InstrumentedTest {
}
}
})
- mTestHelper.await(latch4)
+ testHelper.await(latch4)
// -> It must use the same backup version
assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)
- mTestHelper.doSync {
+ testHelper.doSync {
aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it)
}
@@ -1063,8 +1061,8 @@ class KeysBackupTest : InstrumentedTest {
stateObserver.stopAndCheckStates(null)
stateObserver2.stopAndCheckStates(null)
- mTestHelper.signOutAndClose(aliceSession2)
- cryptoTestData.cleanUp(mTestHelper)
+ testHelper.signOutAndClose(aliceSession2)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -1074,7 +1072,7 @@ class KeysBackupTest : InstrumentedTest {
@Test
fun deleteKeysBackupTest() {
// - Create a backup version
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@@ -1082,17 +1080,17 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled)
- val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
+ val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled)
// Delete the backup
- mTestHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) }
+ testHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) }
// Backup is now disabled
assertFalse(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt
index a625ffc0e9..592b798bcc 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt
@@ -32,8 +32,12 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import java.util.concurrent.CountDownLatch
class KeysBackupTestHelper(
- private val mTestHelper: CommonTestHelper,
- private val mCryptoTestHelper: CryptoTestHelper) {
+ private val testHelper: CommonTestHelper,
+ private val cryptoTestHelper: CryptoTestHelper) {
+
+ fun waitForKeybackUpBatching() {
+ Thread.sleep(400)
+ }
/**
* Common initial condition
@@ -43,7 +47,9 @@ class KeysBackupTestHelper(
* @param password optional password
*/
fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
+
+ waitForKeybackUpBatching()
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@@ -57,7 +63,7 @@ class KeysBackupTestHelper(
var lastProgress = 0
var lastTotal = 0
- mTestHelper.doSync {
+ testHelper.doSync {
keysBackup.backupAllGroupSessions(object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
lastProgress = progress
@@ -72,7 +78,7 @@ class KeysBackupTestHelper(
val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device
- val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
+ val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
// Test check: aliceSession2 has no keys at login
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
@@ -92,7 +98,7 @@ class KeysBackupTestHelper(
password: String? = null): PrepareKeysBackupDataResult {
val stateObserver = StateObserver(keysBackup)
- val megolmBackupCreationInfo = mTestHelper.doSync {
+ val megolmBackupCreationInfo = testHelper.doSync {
keysBackup.prepareKeysBackupVersion(password, null, it)
}
@@ -101,7 +107,7 @@ class KeysBackupTestHelper(
Assert.assertFalse(keysBackup.isEnabled)
// Create the version
- val keysVersion = mTestHelper.doSync {
+ val keysVersion = testHelper.doSync {
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
}
@@ -136,7 +142,7 @@ class KeysBackupTestHelper(
}
})
- mTestHelper.await(latch)
+ testHelper.await(latch)
}
fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) {
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt
index b343d7334a..43f8dc0762 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt
@@ -18,10 +18,6 @@ package org.matrix.android.sdk.internal.crypto.ssss
import androidx.lifecycle.Observer
import androidx.test.ext.junit.runners.AndroidJUnit4
-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.assertNotNull
import org.junit.Assert.assertNull
@@ -36,6 +32,7 @@ import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent
import org.matrix.android.sdk.api.session.securestorage.KeySigner
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
import org.matrix.android.sdk.api.session.securestorage.SecretStorageKeyContent
+import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageError
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo
import org.matrix.android.sdk.api.util.Optional
@@ -45,13 +42,12 @@ import org.matrix.android.sdk.common.TestConstants
import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
-import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class QuadSTests : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
+ private val testHelper = CommonTestHelper(context())
private val emptyKeySigner = object : KeySigner {
override fun sign(canonicalJson: String): Map>? {
@@ -60,35 +56,29 @@ class QuadSTests : InstrumentedTest {
}
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun test_Generate4SKey() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService
val TEST_KEY_ID = "my.test.Key"
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
}
- // Assert Account data is updated
- val accountDataLock = CountDownLatch(1)
var accountData: UserAccountDataEvent? = null
-
- val liveAccountData = runBlocking(Dispatchers.Main) {
- aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
- }
- val accountDataObserver = Observer?> { t ->
- if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
- accountData = t.getOrNull()
- accountDataLock.countDown()
+ // Assert Account data is updated
+ testHelper.waitWithLatch {
+ val liveAccountData = aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
+ val accountDataObserver = Observer?> { t ->
+ if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
+ accountData = t.getOrNull()
+ }
+ it.countDown()
}
+ liveAccountData.observeForever(accountDataObserver)
}
- GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
-
- mTestHelper.await(accountDataLock)
-
assertNotNull("Key should be stored in account data", accountData)
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
assertNotNull("Key Content cannot be parsed", parsed)
@@ -96,36 +86,29 @@ class QuadSTests : InstrumentedTest {
assertEquals("Unexpected key name", "Test Key", parsed.name)
assertNull("Key was not generated from passphrase", parsed.passphrase)
- // Set as default key
- GlobalScope.launch {
- quadS.setDefaultKey(TEST_KEY_ID)
- }
-
var defaultKeyAccountData: UserAccountDataEvent? = null
- val defaultDataLock = CountDownLatch(1)
-
- val liveDefAccountData = runBlocking(Dispatchers.Main) {
- aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
- }
- val accountDefDataObserver = Observer?> { t ->
- if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
- defaultKeyAccountData = t.getOrNull()!!
- defaultDataLock.countDown()
+ // Set as default key
+ testHelper.waitWithLatch { latch ->
+ quadS.setDefaultKey(TEST_KEY_ID)
+ val liveDefAccountData =
+ aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
+ val accountDefDataObserver = Observer?> { t ->
+ if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
+ defaultKeyAccountData = t.getOrNull()!!
+ latch.countDown()
+ }
}
+ liveDefAccountData.observeForever(accountDefDataObserver)
}
- GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) }
-
- mTestHelper.await(defaultDataLock)
-
assertNotNull(defaultKeyAccountData?.content)
assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
- mTestHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(aliceSession)
}
@Test
fun test_StoreSecret() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId = "My.Key"
val info = generatedSecret(aliceSession, keyId, true)
@@ -133,7 +116,7 @@ class QuadSTests : InstrumentedTest {
// Store a secret
val clearSecret = "42".toByteArray().toBase64NoPadding()
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret(
"secret.of.life",
clearSecret,
@@ -154,7 +137,7 @@ class QuadSTests : InstrumentedTest {
// Try to decrypt??
- val decryptedSecret = mTestHelper.runBlockingTest {
+ val decryptedSecret = testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret(
"secret.of.life",
null, // default key
@@ -163,32 +146,32 @@ class QuadSTests : InstrumentedTest {
}
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
- mTestHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(aliceSession)
}
@Test
fun test_SetDefaultLocalEcho() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val quadS = aliceSession.sharedSecretStorageService
val TEST_KEY_ID = "my.test.Key"
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
}
// Test that we don't need to wait for an account data sync to access directly the keyid from DB
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
quadS.setDefaultKey(TEST_KEY_ID)
}
- mTestHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(aliceSession)
}
@Test
fun test_StoreSecretWithMultipleKey() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId1 = "Key.1"
val key1Info = generatedSecret(aliceSession, keyId1, true)
val keyId2 = "Key2"
@@ -196,7 +179,7 @@ class QuadSTests : InstrumentedTest {
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret(
"my.secret",
mySecretText.toByteArray().toBase64NoPadding(),
@@ -216,33 +199,33 @@ class QuadSTests : InstrumentedTest {
assertNotNull(encryptedContent?.get(keyId2))
// Assert that can decrypt with both keys
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1,
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!
)
}
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId2,
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
)
}
- mTestHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(aliceSession)
}
@Test
fun test_GetSecretWithBadPassphrase() {
- val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+ val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val keyId1 = "Key.1"
val passphrase = "The good pass phrase"
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret(
"my.secret",
mySecretText.toByteArray().toBase64NoPadding(),
@@ -250,19 +233,23 @@ class QuadSTests : InstrumentedTest {
)
}
- mTestHelper.runBlockingTest {
- aliceSession.sharedSecretStorageService.getSecret("my.secret",
- keyId1,
- RawBytesKeySpec.fromPassphrase(
- "A bad passphrase",
- key1Info.content?.passphrase?.salt ?: "",
- key1Info.content?.passphrase?.iterations ?: 0,
- null)
- )
+ testHelper.runBlockingTest {
+ try {
+ aliceSession.sharedSecretStorageService.getSecret("my.secret",
+ keyId1,
+ RawBytesKeySpec.fromPassphrase(
+ "A bad passphrase",
+ key1Info.content?.passphrase?.salt ?: "",
+ key1Info.content?.passphrase?.iterations ?: 0,
+ null)
+ )
+ } catch (throwable: Throwable) {
+ assert(throwable is SharedSecretStorageError.BadMac)
+ }
}
// Now try with correct key
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1,
RawBytesKeySpec.fromPassphrase(
@@ -273,42 +260,36 @@ class QuadSTests : InstrumentedTest {
)
}
- mTestHelper.signOutAndClose(aliceSession)
+ testHelper.signOutAndClose(aliceSession)
}
- @Suppress("EXPERIMENTAL_API_USAGE")
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
- val accountDataLock = CountDownLatch(1)
var accountData: UserAccountDataEvent? = null
-
- val liveAccountData = runBlocking(Dispatchers.Main) {
- session.accountDataService().getLiveUserAccountDataEvent(type)
- }
- val accountDataObserver = Observer?> { t ->
- if (t?.getOrNull()?.type == type) {
- accountData = t.getOrNull()
- accountDataLock.countDown()
+ testHelper.waitWithLatch {
+ val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
+ val accountDataObserver = Observer?> { t ->
+ if (t?.getOrNull()?.type == type) {
+ accountData = t.getOrNull()
+ it.countDown()
+ }
}
+ liveAccountData.observeForever(accountDataObserver)
}
- GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
- mTestHelper.await(accountDataLock)
-
assertNotNull("Account Data type:$type should be found", accountData)
-
return accountData!!
}
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService
- val creationInfo = mTestHelper.runBlockingTest {
+ val creationInfo = testHelper.runBlockingTest {
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
}
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
if (asDefault) {
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
quadS.setDefaultKey(keyId)
}
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
@@ -320,7 +301,7 @@ class QuadSTests : InstrumentedTest {
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService
- val creationInfo = mTestHelper.runBlockingTest {
+ val creationInfo = testHelper.runBlockingTest {
quadS.generateKeyWithPassphrase(
keyId,
keyId,
@@ -331,7 +312,7 @@ class QuadSTests : InstrumentedTest {
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
if (asDefault) {
- mTestHelper.runBlockingTest {
+ testHelper.runBlockingTest {
quadS.setDefaultKey(keyId)
}
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt
index e0d49b3f5e..c914da6f71 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt
@@ -53,12 +53,12 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SASTest : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test
fun test_aliceStartThenAliceCancel() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -83,7 +83,7 @@ class SASTest : InstrumentedTest {
val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!)
assertNotNull("Alice should have a started transaction", aliceKeyTx)
- mTestHelper.await(bobTxCreatedLatch)
+ testHelper.await(bobTxCreatedLatch)
bobVerificationService.removeListener(bobListener)
val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)
@@ -116,7 +116,7 @@ class SASTest : InstrumentedTest {
bobVerificationService.addListener(bobListener2)
aliceSasTx.cancel(CancelCode.User)
- mTestHelper.await(cancelLatch)
+ testHelper.await(cancelLatch)
assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled)
assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled)
@@ -133,13 +133,13 @@ class SASTest : InstrumentedTest {
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
@Test
fun test_key_agreement_protocols_must_include_curve25519() {
fail("Not passing for the moment")
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
@@ -186,17 +186,17 @@ class SASTest : InstrumentedTest {
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
- mTestHelper.await(cancelLatch)
+ testHelper.await(cancelLatch)
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
@Test
fun test_key_agreement_macs_Must_include_hmac_sha256() {
fail("Not passing for the moment")
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
@@ -223,18 +223,18 @@ class SASTest : InstrumentedTest {
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
- mTestHelper.await(cancelLatch)
+ testHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
@Test
fun test_key_agreement_short_code_include_decimal() {
fail("Not passing for the moment")
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!!
@@ -261,12 +261,12 @@ class SASTest : InstrumentedTest {
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
- mTestHelper.await(cancelLatch)
+ testHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
private fun fakeBobStart(bobSession: Session,
@@ -303,7 +303,7 @@ class SASTest : InstrumentedTest {
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
@Test
fun test_aliceStartTwoRequests() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -332,10 +332,10 @@ class SASTest : InstrumentedTest {
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
- mTestHelper.await(aliceCreatedLatch)
- mTestHelper.await(aliceCancelledLatch)
+ testHelper.await(aliceCreatedLatch)
+ testHelper.await(aliceCancelledLatch)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
/**
@@ -343,7 +343,7 @@ class SASTest : InstrumentedTest {
*/
@Test
fun test_aliceAndBobAgreement() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -383,7 +383,7 @@ class SASTest : InstrumentedTest {
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
- mTestHelper.await(aliceAcceptedLatch)
+ testHelper.await(aliceAcceptedLatch)
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false)
@@ -397,12 +397,12 @@ class SASTest : InstrumentedTest {
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
}
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
@Test
fun test_aliceAndBobSASCode() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -444,8 +444,8 @@ class SASTest : InstrumentedTest {
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
- mTestHelper.await(aliceSASLatch)
- mTestHelper.await(bobSASLatch)
+ testHelper.await(aliceSASLatch)
+ testHelper.await(bobSASLatch)
val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction
val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction
@@ -453,12 +453,12 @@ class SASTest : InstrumentedTest {
assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
@Test
fun test_happyPath() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -520,8 +520,8 @@ class SASTest : InstrumentedTest {
val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
- mTestHelper.await(aliceSASLatch)
- mTestHelper.await(bobSASLatch)
+ testHelper.await(aliceSASLatch)
+ testHelper.await(bobSASLatch)
// Assert that devices are verified
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId)
@@ -532,12 +532,12 @@ class SASTest : InstrumentedTest {
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
@Test
fun test_ConcurrentStart() {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession
@@ -553,8 +553,8 @@ class SASTest : InstrumentedTest {
var requestID: String? = null
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
requestID = prAlicePOV?.transactionId
Log.v("TEST", "== alicePOV is $prAlicePOV")
@@ -564,8 +564,8 @@ class SASTest : InstrumentedTest {
Log.v("TEST", "== requestID is $requestID")
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
Log.v("TEST", "== prBobPOV is $prBobPOV")
prBobPOV?.transactionId == requestID
@@ -579,8 +579,8 @@ class SASTest : InstrumentedTest {
)
// wait for alice to get the ready
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
@@ -606,22 +606,22 @@ class SASTest : InstrumentedTest {
var alicePovTx: SasVerificationTransaction?
var bobPovTx: SasVerificationTransaction?
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction
Log.v("TEST", "== alicePovTx is $alicePovTx")
alicePovTx?.state == VerificationTxState.ShortCodeReady
}
}
// wait for alice to get the ready
- mTestHelper.waitWithLatch {
- mTestHelper.retryPeriodicallyWithLatch(it) {
+ testHelper.waitWithLatch {
+ testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction
Log.v("TEST", "== bobPovTx is $bobPovTx")
bobPovTx?.state == VerificationTxState.ShortCodeReady
}
}
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt
index 397f7f9441..36306aa383 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt
@@ -40,8 +40,8 @@ import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class VerificationTest : InstrumentedTest {
- private val mTestHelper = CommonTestHelper(context())
- private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+ private val testHelper = CommonTestHelper(context())
+ private val cryptoTestHelper = CryptoTestHelper(testHelper)
data class ExpectedResult(
val sasIsSupported: Boolean = false,
@@ -155,12 +155,12 @@ class VerificationTest : InstrumentedTest {
bobSupportedMethods: List,
expectedResultForAlice: ExpectedResult,
expectedResultForBob: ExpectedResult) {
- val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
+ val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession!!
- mTestHelper.doSync { callback ->
+ testHelper.doSync { callback ->
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -176,7 +176,7 @@ class VerificationTest : InstrumentedTest {
}, callback)
}
- mTestHelper.doSync { callback ->
+ testHelper.doSync { callback ->
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
@@ -234,7 +234,7 @@ class VerificationTest : InstrumentedTest {
val bobUserId = bobSession.myUserId
// Step 1: Alice starts a verification request
aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId)
- mTestHelper.await(latch)
+ testHelper.await(latch)
aliceReadyPendingVerificationRequest!!.let { pr ->
pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported
@@ -248,6 +248,6 @@ class VerificationTest : InstrumentedTest {
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
}
- cryptoTestData.cleanUp(mTestHelper)
+ cryptoTestData.cleanUp(testHelper)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt
index 1adf31be5f..8a4429db45 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt
@@ -45,7 +45,7 @@ object RoomDataHelper {
content: Content? = null,
prevContent: Content? = null,
sender: String = FAKE_TEST_SENDER,
- stateKey: String = FAKE_TEST_SENDER
+ stateKey: String? = null
): Event {
return Event(
type = type,
@@ -64,6 +64,6 @@ object RoomDataHelper {
private fun createFakeRoomMemberEvent(): Event {
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
- return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
+ return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember, stateKey = FAKE_TEST_SENDER)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt
index dfa6ec10ae..bc9722c922 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt
@@ -65,14 +65,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
message,
numberOfMessagesToSend)
- // Alice clear the cache
- commonTestHelper.runBlockingTest {
- aliceSession.clearCache()
- }
-
- // And restarts the sync
- aliceSession.startSync(true)
-
+ // Alice clear the cache and restart the sync
+ commonTestHelper.clearCacheAndSync(aliceSession)
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
aliceTimeline.start()
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt
index 1baf490dfc..45e4b53c77 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt
@@ -24,14 +24,10 @@ import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.session.events.model.toModel
-import org.matrix.android.sdk.api.session.room.model.message.MessageContent
-import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestData
import org.matrix.android.sdk.common.CryptoTestHelper
-import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@@ -84,24 +80,12 @@ class SearchMessagesTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
- val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10))
- aliceTimeline.start()
-
- val lock = CountDownLatch(1)
-
- val eventListener = commonTestHelper.createEventListener(lock) { snapshot ->
- snapshot.count { it.root.content.toModel()?.body?.startsWith(MESSAGE).orFalse() } == 2
- }
-
- aliceTimeline.addListener(eventListener)
commonTestHelper.sendTextMessage(
roomFromAlicePOV,
MESSAGE,
2)
- commonTestHelper.await(lock)
-
val data = commonTestHelper.runBlockingTest {
block.invoke(cryptoTestData)
}
@@ -114,7 +98,6 @@ class SearchMessagesTest : InstrumentedTest {
}.orFalse()
)
- aliceTimeline.removeAllListeners()
cryptoTestData.cleanUp(commonTestHelper)
}
}
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt
index 5911414c25..d7be19295c 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt
@@ -16,9 +16,7 @@
package org.matrix.android.sdk.session.space
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
@@ -50,18 +48,15 @@ class SpaceCreationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun createSimplePublicSpace() {
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
val roomName = "My Space"
val topic = "A public space for test"
var spaceId: String = ""
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- spaceId = session.spaceService().createSpace(roomName, topic, null, true)
- // wait a bit to let the summary update it self :/
- it.countDown()
- }
+ spaceId = session.spaceService().createSpace(roomName, topic, null, true)
+ // wait a bit to let the summary update it self :/
+ it.countDown()
}
val syncedSpace = session.spaceService().getSpace(spaceId)
@@ -134,7 +129,6 @@ class SpaceCreationTest : InstrumentedTest {
}
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun testSimplePublicSpaceWithChildren() {
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
@@ -148,50 +142,40 @@ class SpaceCreationTest : InstrumentedTest {
// create a room
var firstChild: String? = null
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- firstChild = aliceSession.createRoom(CreateRoomParams().apply {
- this.name = "FirstRoom"
- this.topic = "Description of first room"
- this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
- })
- it.countDown()
- }
+ firstChild = aliceSession.createRoom(CreateRoomParams().apply {
+ this.name = "FirstRoom"
+ this.topic = "Description of first room"
+ this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
+ })
+ it.countDown()
}
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true)
- it.countDown()
- }
+ syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true)
+ it.countDown()
}
var secondChild: String? = null
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- secondChild = aliceSession.createRoom(CreateRoomParams().apply {
- this.name = "SecondRoom"
- this.topic = "Description of second room"
- this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
- })
- it.countDown()
- }
+ secondChild = aliceSession.createRoom(CreateRoomParams().apply {
+ this.name = "SecondRoom"
+ this.topic = "Description of second room"
+ this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
+ })
+ it.countDown()
}
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true)
- it.countDown()
- }
+ syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true)
+ it.countDown()
}
// Try to join from bob, it's a public space no need to invite
var joinResult: JoinSpaceResult? = null
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- joinResult = bobSession.spaceService().joinSpace(spaceId)
- // wait a bit to let the summary update it self :/
- it.countDown()
- }
+ joinResult = bobSession.spaceService().joinSpace(spaceId)
+ // wait a bit to let the summary update it self :/
+ it.countDown()
}
assertEquals(JoinSpaceResult.Success, joinResult)
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt
index 436daf001b..1c38edbbd9 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt
@@ -18,9 +18,6 @@ package org.matrix.android.sdk.session.space
import android.util.Log
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.assertNotNull
@@ -56,43 +53,34 @@ class SpaceHierarchyTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context())
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun createCanonicalChildRelation() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
val spaceName = "My Space"
val topic = "A public space for test"
- var spaceId: String = ""
+ var spaceId = ""
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
- it.countDown()
- }
+ spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
+ it.countDown()
}
val syncedSpace = session.spaceService().getSpace(spaceId)
- var roomId: String = ""
+ var roomId = ""
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- roomId = session.createRoom(CreateRoomParams().apply { name = "General" })
- it.countDown()
- }
+ roomId = session.createRoom(CreateRoomParams().apply { name = "General" })
+ it.countDown()
}
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- syncedSpace!!.addChildren(roomId, viaServers, null, true)
- it.countDown()
- }
+ syncedSpace!!.addChildren(roomId, viaServers, null, true)
+ it.countDown()
}
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
- it.countDown()
- }
+ session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
+ it.countDown()
}
Thread.sleep(9000)
@@ -181,7 +169,6 @@ class SpaceHierarchyTest : InstrumentedTest {
// }
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun testFilteringBySpace() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@@ -205,29 +192,23 @@ class SpaceHierarchyTest : InstrumentedTest {
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
- session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
- it.countDown()
- }
+ spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
+ session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
+ it.countDown()
}
// Create orphan rooms
var orphan1 = ""
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" })
- it.countDown()
- }
+ orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" })
+ it.countDown()
}
var orphan2 = ""
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" })
- it.countDown()
- }
+ orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" })
+ it.countDown()
}
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) })
@@ -250,11 +231,9 @@ class SpaceHierarchyTest : InstrumentedTest {
// Add a non canonical child and check that it does not appear as orphan
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" })
- spaceA!!.addChildren(a3, viaServers, null, false)
- it.countDown()
- }
+ val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" })
+ spaceA!!.addChildren(a3, viaServers, null, false)
+ it.countDown()
}
Thread.sleep(2_000)
@@ -265,7 +244,6 @@ class SpaceHierarchyTest : InstrumentedTest {
}
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun testBreakCycle() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@@ -283,20 +261,16 @@ class SpaceHierarchyTest : InstrumentedTest {
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
- session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
- it.countDown()
- }
+ spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
+ session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
+ it.countDown()
}
// add back A as subspace of C
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
- spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
- it.countDown()
- }
+ val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
+ spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
+ it.countDown()
}
Thread.sleep(1000)
@@ -313,7 +287,6 @@ class SpaceHierarchyTest : InstrumentedTest {
}
@Test
- @Suppress("EXPERIMENTAL_API_USAGE")
fun testLiveFlatChildren() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@@ -336,12 +309,14 @@ class SpaceHierarchyTest : InstrumentedTest {
session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
}
- val flatAChildren = runBlocking(Dispatchers.Main) {
- session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
- }
+ val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
+ Triple("C1", true /*auto-join*/, true/*canonical*/),
+ Triple("C2", true, true)
+ ))
commonTestHelper.waitWithLatch { latch ->
+ val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
val childObserver = object : Observer> {
override fun onChanged(children: List?) {
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}")
@@ -354,20 +329,13 @@ class SpaceHierarchyTest : InstrumentedTest {
}
}
- val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
- Triple("C1", true /*auto-join*/, true/*canonical*/),
- Triple("C2", true, true)
- ))
-
// add C as subspace of B
- runBlocking {
- val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
- spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
- }
+ val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
+ spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
// C1 and C2 should be in flatten child of A now
- GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
+ flatAChildren.observeForever(childObserver)
}
// Test part one of the rooms
@@ -376,7 +344,7 @@ class SpaceHierarchyTest : InstrumentedTest {
val bRoom = session.getRoom(bRoomId)
commonTestHelper.waitWithLatch { latch ->
-
+ val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
val childObserver = object : Observer> {
override fun onChanged(children: List?) {
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
@@ -389,13 +357,10 @@ class SpaceHierarchyTest : InstrumentedTest {
}
// part from b room
- runBlocking {
- bRoom!!.leave(null)
- }
+ bRoom!!.leave(null)
// The room should have disapear from flat children
- GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
+ flatAChildren.observeForever(childObserver)
}
-
commonTestHelper.signOutAndClose(session)
}
@@ -404,94 +369,66 @@ class SpaceHierarchyTest : InstrumentedTest {
val roomIds: List
)
- @Suppress("EXPERIMENTAL_API_USAGE")
private fun createPublicSpace(session: Session,
spaceName: String,
childInfo: List>
/** Name, auto-join, canonical*/
): TestSpaceCreationResult {
var spaceId = ""
- commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
- it.countDown()
+ var roomIds: List = emptyList()
+ commonTestHelper.waitWithLatch { latch ->
+ spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
+ val syncedSpace = session.spaceService().getSpace(spaceId)
+ val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
+
+ roomIds = childInfo.map { entry ->
+ session.createRoom(CreateRoomParams().apply { name = entry.first })
}
- }
-
- val syncedSpace = session.spaceService().getSpace(spaceId)
- val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
-
- val roomIds =
- childInfo.map { entry ->
- var roomId = ""
- commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- roomId = session.createRoom(CreateRoomParams().apply { name = entry.first })
- it.countDown()
- }
- }
- roomId
- }
-
- roomIds.forEachIndexed { index, roomId ->
- runBlocking {
+ roomIds.forEachIndexed { index, roomId ->
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
val canonical = childInfo[index].third
if (canonical != null) {
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
}
}
+ latch.countDown()
}
return TestSpaceCreationResult(spaceId, roomIds)
}
- @Suppress("EXPERIMENTAL_API_USAGE")
private fun createPrivateSpace(session: Session,
spaceName: String,
childInfo: List>
/** Name, auto-join, canonical*/
): TestSpaceCreationResult {
var spaceId = ""
- commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
- it.countDown()
- }
- }
-
- val syncedSpace = session.spaceService().getSpace(spaceId)
- val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
-
- val roomIds =
- childInfo.map { entry ->
- var roomId = ""
- commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- val homeServerCapabilities = session
- .getHomeServerCapabilities()
- roomId = session.createRoom(CreateRoomParams().apply {
- name = entry.first
- this.featurePreset = RestrictedRoomPreset(
- homeServerCapabilities,
- listOf(
- RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
- )
- )
- })
- it.countDown()
- }
+ var roomIds: List = emptyList()
+ commonTestHelper.waitWithLatch { latch ->
+ spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
+ val syncedSpace = session.spaceService().getSpace(spaceId)
+ val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
+ roomIds =
+ childInfo.map { entry ->
+ val homeServerCapabilities = session
+ .getHomeServerCapabilities()
+ session.createRoom(CreateRoomParams().apply {
+ name = entry.first
+ this.featurePreset = RestrictedRoomPreset(
+ homeServerCapabilities,
+ listOf(
+ RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
+ )
+ )
+ })
}
- roomId
- }
-
- roomIds.forEachIndexed { index, roomId ->
- runBlocking {
+ roomIds.forEachIndexed { index, roomId ->
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
val canonical = childInfo[index].third
if (canonical != null) {
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
}
}
+ latch.countDown()
}
return TestSpaceCreationResult(spaceId, roomIds)
}
@@ -559,11 +496,9 @@ class SpaceHierarchyTest : InstrumentedTest {
var bobRoomId = ""
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
- bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
- it.countDown()
- }
+ bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
+ bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
+ it.countDown()
}
commonTestHelper.runBlockingTest {
@@ -577,10 +512,8 @@ class SpaceHierarchyTest : InstrumentedTest {
}
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
- it.countDown()
- }
+ bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
+ it.countDown()
}
commonTestHelper.waitWithLatch { latch ->
@@ -600,19 +533,17 @@ class SpaceHierarchyTest : InstrumentedTest {
// Let's now try to make alice admin of the room
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- val room = bobSession.getRoom(bobRoomId)!!
- val currentPLContent = room
- .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
- ?.let { it.content.toModel() }
+ val room = bobSession.getRoom(bobRoomId)!!
+ val currentPLContent = room
+ .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
+ ?.let { it.content.toModel() }
- val newPowerLevelsContent = currentPLContent
- ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
- ?.toContent()
+ val newPowerLevelsContent = currentPLContent
+ ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
+ ?.toContent()
- room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
- it.countDown()
- }
+ room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
+ it.countDown()
}
commonTestHelper.waitWithLatch { latch ->
@@ -627,10 +558,8 @@ class SpaceHierarchyTest : InstrumentedTest {
}
commonTestHelper.waitWithLatch {
- GlobalScope.launch {
- aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
- it.countDown()
- }
+ aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
+ it.countDown()
}
commonTestHelper.waitWithLatch { latch ->
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt
index 8a4526a5e1..901ba75d16 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt
@@ -20,6 +20,7 @@ import android.content.Context
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Configuration
import androidx.work.WorkManager
+import androidx.work.WorkerFactory
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.auth.AuthenticationService
@@ -33,6 +34,7 @@ import org.matrix.android.sdk.internal.di.DaggerMatrixComponent
import org.matrix.android.sdk.internal.network.ApiInterceptor
import org.matrix.android.sdk.internal.network.UserAgentHolder
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
+import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
@@ -53,12 +55,17 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var sessionManager: SessionManager
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
@Inject internal lateinit var apiInterceptor: ApiInterceptor
+ @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
init {
Monarchy.init(context)
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) {
- WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
+ val configuration = Configuration.Builder()
+ .setExecutor(Executors.newCachedThreadPool())
+ .setWorkerFactory(matrixWorkerFactory)
+ .build()
+ WorkManager.initialize(context, configuration)
}
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
@@ -77,6 +84,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return legacySessionImporter
}
+ fun workerFactory(): WorkerFactory = matrixWorkerFactory
+
fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) {
apiInterceptor.addListener(path, listener)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt
index a39ca5b4f4..0c77b574e7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt
@@ -104,6 +104,8 @@ object EventType {
// Poll
const val POLL_START = "org.matrix.msc3381.poll.start"
+ const val POLL_RESPONSE = "org.matrix.msc3381.poll.response"
+ const val POLL_END = "org.matrix.msc3381.poll.end"
// Unwedging
internal const val DUMMY = "m.dummy"
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PollSummaryContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PollSummaryContent.kt
index 844ef6c1c8..f1e4354314 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PollSummaryContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PollSummaryContent.kt
@@ -24,25 +24,24 @@ import com.squareup.moshi.JsonClass
*/
@JsonClass(generateAdapter = true)
data class PollSummaryContent(
- // Index of my vote
- var myVote: Int? = null,
+ var myVote: String? = null,
// Array of VoteInfo, list is constructed so that there is only one vote by user
// And that optionIndex is valid
- var votes: List? = null
-) {
+ var votes: List? = null,
+ var votesSummary: Map? = null,
+ var totalVotes: Int = 0,
+ var winnerVoteCount: Int = 0
+)
- fun voteCount(): Int {
- return votes?.size ?: 0
- }
-
- fun voteCountForOption(optionIndex: Int): Int {
- return votes?.filter { it.optionIndex == optionIndex }?.count() ?: 0
- }
-}
+@JsonClass(generateAdapter = true)
+data class VoteSummary(
+ val total: Int = 0,
+ val percentage: Double = 0.0
+)
@JsonClass(generateAdapter = true)
data class VoteInfo(
val userId: String,
- val optionIndex: Int,
+ val option: String,
val voteTimestamp: Long
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEndPollContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEndPollContent.kt
new file mode 100644
index 0000000000..491b71477e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEndPollContent.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.session.room.model.message
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
+
+/**
+ * Class representing the org.matrix.msc3381.poll.end event content
+ */
+@JsonClass(generateAdapter = true)
+data class MessageEndPollContent(
+ @Json(name = "m.relates_to") val relatesTo: RelationDefaultContent? = null
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt
deleted file mode 100644
index 7a1a99bd5f..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.matrix.android.sdk.api.session.room.model.message
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.api.session.events.model.Content
-import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
-
-// Possible values for optionType
-const val OPTION_TYPE_POLL = "org.matrix.poll"
-const val OPTION_TYPE_BUTTONS = "org.matrix.buttons"
-
-/**
- * Polls and bot buttons are m.room.message events with a msgtype of m.options,
- * Ref: https://github.com/matrix-org/matrix-doc/pull/2192
- */
-@JsonClass(generateAdapter = true)
-data class MessageOptionsContent(
- @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_OPTIONS,
- @Json(name = "type") val optionType: String? = null,
- @Json(name = "body") override val body: String,
- @Json(name = "label") val label: String?,
- @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
- @Json(name = "options") val options: List? = null,
- @Json(name = "m.new_content") override val newContent: Content? = null
-) : MessageContent
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt
index ef2fd1867a..a4e1317290 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt
@@ -18,8 +18,18 @@ package org.matrix.android.sdk.api.session.room.model.message
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.session.events.model.Content
+import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
@JsonClass(generateAdapter = true)
data class MessagePollContent(
- @Json(name = "org.matrix.msc3381.poll.start") val pollCreationInfo: PollCreationInfo? = null
-)
+ /**
+ * Local message type, not from server
+ */
+ @Transient
+ override val msgType: String = MessageType.MSGTYPE_POLL_START,
+ @Json(name = "body") override val body: String = "",
+ @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
+ @Json(name = "m.new_content") override val newContent: Content? = null,
+ @Json(name = "org.matrix.msc3381.poll.start") val pollCreationInfo: PollCreationInfo? = null
+) : MessageContent
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt
index 9edfe118b0..f3b4e3dc23 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt
@@ -21,13 +21,15 @@ import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
-/**
- * Ref: https://github.com/matrix-org/matrix-doc/pull/2192
- */
@JsonClass(generateAdapter = true)
data class MessagePollResponseContent(
- @Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE,
- @Json(name = "body") override val body: String,
+ /**
+ * Local message type, not from server
+ */
+ @Transient
+ override val msgType: String = MessageType.MSGTYPE_POLL_RESPONSE,
+ @Json(name = "body") override val body: String = "",
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
- @Json(name = "m.new_content") override val newContent: Content? = null
+ @Json(name = "m.new_content") override val newContent: Content? = null,
+ @Json(name = "org.matrix.msc3381.poll.response") val response: PollResponse? = null
) : MessageContent
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt
index 1e8959afc3..2a6138ae60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt
@@ -25,15 +25,18 @@ object MessageType {
const val MSGTYPE_VIDEO = "m.video"
const val MSGTYPE_LOCATION = "m.location"
const val MSGTYPE_FILE = "m.file"
- const val MSGTYPE_OPTIONS = "org.matrix.options"
- const val MSGTYPE_RESPONSE = "org.matrix.response"
- const val MSGTYPE_POLL_CLOSED = "org.matrix.poll_closed"
+
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
// Because sticker isn't a message type but a event type without msgtype field
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
+ // Fake message types for poll events to be able to inherit them from MessageContent
+ // Because poll events are not message events and they don't hanve msgtype field
+ const val MSGTYPE_POLL_START = "org.matrix.android.sdk.poll.start"
+ const val MSGTYPE_POLL_RESPONSE = "org.matrix.android.sdk.poll.response"
+
const val MSGTYPE_CONFETTI = "nic.custom.confetti"
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/OptionItem.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollResponse.kt
similarity index 75%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/OptionItem.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollResponse.kt
index 625043df87..ddeec5cd5b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/OptionItem.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollResponse.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -19,11 +19,7 @@ package org.matrix.android.sdk.api.session.room.model.message
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-/**
- * Ref: https://github.com/matrix-org/matrix-doc/pull/2192
- */
@JsonClass(generateAdapter = true)
-data class OptionItem(
- @Json(name = "label") val label: String?,
- @Json(name = "value") val value: String?
+data class PollResponse(
+ @Json(name = "answers") val answers: List? = null
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index a2b38b6606..5b387c3413 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -91,11 +91,17 @@ interface SendService {
/**
* Method to send a poll response.
* @param pollEventId the poll currently replied to
- * @param optionIndex The reply index
- * @param optionValue The option value (for compatibility)
+ * @param answerId The id of the answer
* @return a [Cancelable]
*/
- fun sendOptionsReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable
+ fun voteToPoll(pollEventId: String, answerId: String): Cancelable
+
+ /**
+ * End a poll in the room.
+ * @param pollEventId event id of the poll
+ * @return a [Cancelable]
+ */
+ fun endPoll(pollEventId: String): Cancelable
/**
* Redact (delete) the given event.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt
index 4dea48eff7..f81a1a62fa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt
@@ -32,7 +32,8 @@ object RoomSummaryConstants {
EventType.CALL_ANSWER,
EventType.ENCRYPTED,
EventType.STICKER,
- EventType.REACTION
+ EventType.REACTION,
+ EventType.POLL_START
)
// SC addition | this is the Element behaviour previous to Element v1.0.7
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
index 86cb10bfe8..932439c81c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
@@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
+import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
@@ -126,10 +127,10 @@ fun TimelineEvent.getEditedEventId(): String? {
* Get last MessageContent, after a possible edition
*/
fun TimelineEvent.getLastMessageContent(): MessageContent? {
- return if (root.getClearType() == EventType.STICKER) {
- root.getClearContent().toModel()
- } else {
- (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
+ return when (root.getClearType()) {
+ EventType.STICKER -> root.getClearContent().toModel()
+ EventType.POLL_START -> root.getClearContent().toModel()
+ else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt
index 876e99da63..d7dff72288 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt
@@ -58,6 +58,14 @@ data class SyncResponse(
@Json(name = "device_one_time_keys_count")
val deviceOneTimeKeysCount: DeviceOneTimeKeysCountSyncResponse? = null,
+ /**
+ * The key algorithms for which the server has an unused fallback key for the device.
+ * If the client wants the server to have a fallback key for a given key algorithm,
+ * but that algorithm is not listed in device_unused_fallback_key_types, the client will upload a new key.
+ */
+ @Json(name = "org.matrix.msc2732.device_unused_fallback_key_types")
+ val deviceUnusedFallbackKeyTypes: List? = null,
+
/**
* List of groups.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt
index 10ce0829d0..e64cf1872e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt
@@ -16,6 +16,8 @@
package org.matrix.android.sdk.api.session.terms
+import org.matrix.android.sdk.internal.session.terms.TermsResponse
+
interface TermsService {
enum class ServiceType {
IntegrationManager,
@@ -28,4 +30,10 @@ interface TermsService {
baseUrl: String,
agreedUrls: List,
token: String?)
+
+ /**
+ * Get the homeserver terms, from the register API.
+ * Will be updated once https://github.com/matrix-org/matrix-doc/pull/3012 will be implemented.
+ */
+ suspend fun getHomeserverTerms(baseUrl: String): TermsResponse
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt
index c11d00278b..3a5f8e7668 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
@@ -34,9 +35,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject
-internal class CancelGossipRequestWorker(context: Context,
- params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class CancelGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
index 18f344882c..7d9c351410 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
@@ -67,6 +67,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.model.MXDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
+import org.matrix.android.sdk.internal.crypto.model.MXKey.Companion.KEY_SIGNED_CURVE_25519_TYPE
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
@@ -431,6 +432,14 @@ internal class DefaultCryptoService @Inject constructor(
if (isStarted()) {
// Make sure we process to-device messages before generating new one-time-keys #2782
deviceListManager.refreshOutdatedDeviceLists()
+ // The presence of device_unused_fallback_key_types indicates that the server supports fallback keys.
+ // If there's no unused signed_curve25519 fallback key we need a new one.
+ if (syncResponse.deviceUnusedFallbackKeyTypes != null &&
+ // Generate a fallback key only if the server does not already have an unused fallback key.
+ !syncResponse.deviceUnusedFallbackKeyTypes.contains(KEY_SIGNED_CURVE_25519_TYPE)) {
+ oneTimeKeysUploader.needsNewFallback()
+ }
+
oneTimeKeysUploader.maybeUploadOneTimeKeys()
incomingGossipingRequestManager.processReceivedGossipingRequests()
}
@@ -928,7 +937,7 @@ internal class DefaultCryptoService @Inject constructor(
signatures = objectSigner.signObject(canonicalJson)
)
- val uploadDeviceKeysParams = UploadKeysTask.Params(rest, null)
+ val uploadDeviceKeysParams = UploadKeysTask.Params(rest, null, null)
uploadKeysTask.execute(uploadDeviceKeysParams)
cryptoStore.setDeviceKeysUploaded(true)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt
index 441dfe4a5d..e1a706df79 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt
@@ -136,6 +136,51 @@ internal class MXOlmDevice @Inject constructor(
return store.getOlmAccount().maxOneTimeKeys()
}
+ /**
+ * Returns an unpublished fallback key
+ * A call to markKeysAsPublished will mark it as published and this
+ * call will return null (until a call to generateFallbackKey is made)
+ */
+ fun getFallbackKey(): MutableMap>? {
+ try {
+ return store.getOlmAccount().fallbackKey()
+ } catch (e: Exception) {
+ Timber.e("## getFallbackKey() : failed")
+ }
+ return null
+ }
+
+ /**
+ * Generates a new fallback key if there is not already
+ * an unpublished one.
+ * @return true if a new key was generated
+ */
+ fun generateFallbackKeyIfNeeded(): Boolean {
+ try {
+ if (!hasUnpublishedFallbackKey()) {
+ store.getOlmAccount().generateFallbackKey()
+ store.saveOlmAccount()
+ return true
+ }
+ } catch (e: Exception) {
+ Timber.e("## generateFallbackKey() : failed")
+ }
+ return false
+ }
+
+ internal fun hasUnpublishedFallbackKey(): Boolean {
+ return getFallbackKey()?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY).orEmpty().isNotEmpty()
+ }
+
+ fun forgetFallbackKey() {
+ try {
+ store.getOlmAccount().forgetFallbackKey()
+ store.saveOlmAccount()
+ } catch (e: Exception) {
+ Timber.e("## forgetFallbackKey() : failed")
+ }
+ }
+
/**
* Release the instance
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt
index c4b62fe9fe..4aebe091c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt
@@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.crypto
+import android.content.Context
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.model.MXKey
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
@@ -28,11 +29,16 @@ import javax.inject.Inject
import kotlin.math.floor
import kotlin.math.min
+// The spec recommend a 5mn delay, but due to federation
+// or server downtime we give it a bit more time (1 hour)
+const val FALLBACK_KEY_FORGET_DELAY = 60 * 60_000L
+
@SessionScope
internal class OneTimeKeysUploader @Inject constructor(
private val olmDevice: MXOlmDevice,
private val objectSigner: ObjectSigner,
- private val uploadKeysTask: UploadKeysTask
+ private val uploadKeysTask: UploadKeysTask,
+ context: Context
) {
// tell if there is a OTK check in progress
private var oneTimeKeyCheckInProgress = false
@@ -41,6 +47,9 @@ internal class OneTimeKeysUploader @Inject constructor(
private var lastOneTimeKeyCheck: Long = 0
private var oneTimeKeyCount: Int? = null
+ // Simple storage to remember when was uploaded the last fallback key
+ private val storage = context.getSharedPreferences("OneTimeKeysUploader_${olmDevice.deviceEd25519Key.hashCode()}", Context.MODE_PRIVATE)
+
/**
* Stores the current one_time_key count which will be handled later (in a call of
* _onSyncCompleted). The count is e.g. coming from a /sync response.
@@ -51,6 +60,15 @@ internal class OneTimeKeysUploader @Inject constructor(
oneTimeKeyCount = currentCount
}
+ fun needsNewFallback() {
+ if (olmDevice.generateFallbackKeyIfNeeded()) {
+ // As we generated a new one, it's already forgetting one
+ // so we can clear the last publish time
+ // (in case the network calls fails after to avoid calling forgetKey)
+ saveLastFallbackKeyPublishTime(0L)
+ }
+ }
+
/**
* Check if the OTK must be uploaded.
*/
@@ -65,9 +83,19 @@ internal class OneTimeKeysUploader @Inject constructor(
return
}
- lastOneTimeKeyCheck = System.currentTimeMillis()
oneTimeKeyCheckInProgress = true
+ val oneTimeKeyCountFromSync = oneTimeKeyCount
+ ?: fetchOtkCount() // we don't have count from sync so get from server
+ ?: return Unit.also {
+ oneTimeKeyCheckInProgress = false
+ Timber.w("maybeUploadOneTimeKeys: Failed to get otk count from server")
+ }
+
+ Timber.d("maybeUploadOneTimeKeys: otk count $oneTimeKeyCountFromSync , unpublished fallback key ${olmDevice.hasUnpublishedFallbackKey()}")
+
+ lastOneTimeKeyCheck = System.currentTimeMillis()
+
// We then check how many keys we can store in the Account object.
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
@@ -78,37 +106,37 @@ internal class OneTimeKeysUploader @Inject constructor(
// discard the oldest private keys first. This will eventually clean
// out stale private keys that won't receive a message.
val keyLimit = floor(maxOneTimeKeys / 2.0).toInt()
- if (oneTimeKeyCount == null) {
- // Ask the server how many otk he has
- oneTimeKeyCount = fetchOtkCount()
- }
- val oneTimeKeyCountFromSync = oneTimeKeyCount
- if (oneTimeKeyCountFromSync != null) {
- // We need to keep a pool of one time public keys on the server so that
- // other devices can start conversations with us. But we can only store
- // a finite number of private keys in the olm Account object.
- // To complicate things further then can be a delay between a device
- // claiming a public one time key from the server and it sending us a
- // message. We need to keep the corresponding private key locally until
- // we receive the message.
- // But that message might never arrive leaving us stuck with duff
- // private keys clogging up our local storage.
- // So we need some kind of engineering compromise to balance all of
- // these factors.
- tryOrNull("Unable to upload OTK") {
- val uploadedKeys = uploadOTK(oneTimeKeyCountFromSync, keyLimit)
- Timber.v("## uploadKeys() : success, $uploadedKeys key(s) sent")
- }
- } else {
- Timber.w("maybeUploadOneTimeKeys: waiting to know the number of OTK from the sync")
- lastOneTimeKeyCheck = 0
+
+ // We need to keep a pool of one time public keys on the server so that
+ // other devices can start conversations with us. But we can only store
+ // a finite number of private keys in the olm Account object.
+ // To complicate things further then can be a delay between a device
+ // claiming a public one time key from the server and it sending us a
+ // message. We need to keep the corresponding private key locally until
+ // we receive the message.
+ // But that message might never arrive leaving us stuck with duff
+ // private keys clogging up our local storage.
+ // So we need some kind of engineering compromise to balance all of
+ // these factors.
+ tryOrNull("Unable to upload OTK") {
+ val uploadedKeys = uploadOTK(oneTimeKeyCountFromSync, keyLimit)
+ Timber.v("## uploadKeys() : success, $uploadedKeys key(s) sent")
}
oneTimeKeyCheckInProgress = false
+
+ // Check if we need to forget a fallback key
+ val latestPublishedTime = getLastFallbackKeyPublishTime()
+ if (latestPublishedTime != 0L && System.currentTimeMillis() - latestPublishedTime > FALLBACK_KEY_FORGET_DELAY) {
+ // This should be called once you are reasonably certain that you will not receive any more messages
+ // that use the old fallback key
+ Timber.d("## forgetFallbackKey()")
+ olmDevice.forgetFallbackKey()
+ }
}
private suspend fun fetchOtkCount(): Int? {
return tryOrNull("Unable to get OTK count") {
- val result = uploadKeysTask.execute(UploadKeysTask.Params(null, null))
+ val result = uploadKeysTask.execute(UploadKeysTask.Params(null, null, null))
result.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
}
}
@@ -121,24 +149,47 @@ internal class OneTimeKeysUploader @Inject constructor(
* @return the number of uploaded keys
*/
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Int {
- if (keyLimit <= keyCount) {
+ if (keyLimit <= keyCount && !olmDevice.hasUnpublishedFallbackKey()) {
// If we don't need to generate any more keys then we are done.
return 0
}
- val keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
- olmDevice.generateOneTimeKeys(keysThisLoop)
+ var keysThisLoop = 0
+ if (keyLimit > keyCount) {
+ // Creating keys can be an expensive operation so we limit the
+ // number we generate in one go to avoid blocking the application
+ // for too long.
+ keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
+ olmDevice.generateOneTimeKeys(keysThisLoop)
+ }
+
+ // We check before sending if there is an unpublished key in order to saveLastFallbackKeyPublishTime if needed
+ val hadUnpublishedFallbackKey = olmDevice.hasUnpublishedFallbackKey()
val response = uploadOneTimeKeys(olmDevice.getOneTimeKeys())
olmDevice.markKeysAsPublished()
+ if (hadUnpublishedFallbackKey) {
+ // It had an unpublished fallback key that was published just now
+ saveLastFallbackKeyPublishTime(System.currentTimeMillis())
+ }
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
// Maybe upload other keys
- return keysThisLoop + uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
+ return keysThisLoop +
+ uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) +
+ (if (hadUnpublishedFallbackKey) 1 else 0)
} else {
Timber.e("## uploadOTK() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")
}
}
+ private fun saveLastFallbackKeyPublishTime(timeMillis: Long) {
+ storage.edit().putLong("last_fb_key_publish", timeMillis).apply()
+ }
+
+ private fun getLastFallbackKeyPublishTime(): Long {
+ return storage.getLong("last_fb_key_publish", 0)
+ }
+
/**
* Upload curve25519 one time keys.
*/
@@ -159,10 +210,26 @@ internal class OneTimeKeysUploader @Inject constructor(
oneTimeJson["signed_curve25519:$key_id"] = k
}
+ val fallbackJson = mutableMapOf()
+ val fallbackCurve25519Map = olmDevice.getFallbackKey()?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY).orEmpty()
+ fallbackCurve25519Map.forEach { (key_id, key) ->
+ val k = mutableMapOf()
+ k["key"] = key
+ k["fallback"] = true
+ val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, k)
+ k["signatures"] = objectSigner.signObject(canonicalJson)
+
+ fallbackJson["signed_curve25519:$key_id"] = k
+ }
+
// For now, we set the device id explicitly, as we may not be using the
// same one as used in login.
- val uploadParams = UploadKeysTask.Params(null, oneTimeJson)
- return uploadKeysTask.execute(uploadParams)
+ val uploadParams = UploadKeysTask.Params(
+ deviceKeys = null,
+ oneTimeKeys = oneTimeJson,
+ fallbackKeys = fallbackJson.takeIf { fallbackJson.isNotEmpty() }
+ )
+ return uploadKeysTask.executeRetry(uploadParams, 3)
}
companion object {
@@ -173,6 +240,6 @@ internal class OneTimeKeysUploader @Inject constructor(
private const val ONE_TIME_KEY_GENERATION_MAX_NUMBER = 5
// frequency with which to check & upload one-time keys
- private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60 * 1000).toLong() // one minute
+ private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60_000).toLong() // one minute
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt
index b2ba189b65..3129ccae3b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
@@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
-internal class SendGossipRequestWorker(context: Context,
- params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class SendGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt
index b96943e4ae..ff206a3c96 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
@@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
-internal class SendGossipWorker(context: Context,
- params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class SendGossipWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
index 3326d3707a..5cd647ff6f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
@@ -25,6 +25,7 @@ import io.realm.kotlin.where
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
@@ -50,9 +51,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
-internal class UpdateTrustWorker(context: Context,
- params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysUploadBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysUploadBody.kt
index 69b3992374..363dee9a8d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysUploadBody.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/KeysUploadBody.kt
@@ -40,5 +40,12 @@ internal data class KeysUploadBody(
* May be absent if no new one-time keys are required.
*/
@Json(name = "one_time_keys")
- val oneTimeKeys: JsonDict? = null
+ val oneTimeKeys: JsonDict? = null,
+
+ /**
+ * If the user had previously uploaded a fallback key for a given algorithm, it is replaced.
+ * The server will only keep one fallback key per algorithm for each user.
+ */
+ @Json(name = "org.matrix.msc2732.fallback_keys")
+ val fallbackKeys: JsonDict? = null
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt
index cac4dadd93..30de8e871a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadKeysTask.kt
@@ -32,7 +32,8 @@ internal interface UploadKeysTask : Task $body")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt
index 481ce85f70..a763c05e07 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt
@@ -20,6 +20,7 @@ import androidx.work.Data
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.shouldBeRetried
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker
@@ -33,9 +34,8 @@ import javax.inject.Inject
* Possible previous worker: None
* Possible next worker : None
*/
-internal class SendVerificationMessageWorker(context: Context,
- params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class SendVerificationMessageWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
index 81a067f2c0..d9a4f1bde1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
@@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.session.TestInterceptor
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.system.SystemModule
+import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager
import java.io.File
@@ -86,6 +87,8 @@ internal interface MatrixComponent {
fun sessionManager(): SessionManager
+ fun matrixWorkerFactory(): MatrixWorkerFactory
+
fun inject(matrix: Matrix)
@Component.Factory
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt
index 074f8dc43e..9e50e9efe8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MoshiProvider.kt
@@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
import org.matrix.android.sdk.api.session.room.model.message.MessageNoticeContent
-import org.matrix.android.sdk.api.session.room.model.message.MessageOptionsContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
@@ -57,8 +56,7 @@ object MoshiProvider {
.registerSubtype(MessageLocationContent::class.java, MessageType.MSGTYPE_LOCATION)
.registerSubtype(MessageFileContent::class.java, MessageType.MSGTYPE_FILE)
.registerSubtype(MessageVerificationRequestContent::class.java, MessageType.MSGTYPE_VERIFICATION_REQUEST)
- .registerSubtype(MessageOptionsContent::class.java, MessageType.MSGTYPE_OPTIONS)
- .registerSubtype(MessagePollResponseContent::class.java, MessageType.MSGTYPE_RESPONSE)
+ .registerSubtype(MessagePollResponseContent::class.java, MessageType.MSGTYPE_POLL_RESPONSE)
)
.add(SerializeNulls.JSON_ADAPTER_FACTORY)
.build()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt
index 210eadeff7..b136041f79 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt
@@ -20,6 +20,8 @@ import dagger.Module
import dagger.Provides
import org.matrix.android.sdk.internal.session.MockHttpInterceptor
import org.matrix.android.sdk.internal.session.TestInterceptor
+import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
+import org.matrix.android.sdk.internal.util.DefaultBackgroundDetectionObserver
@Module
internal object NoOpTestModule {
@@ -28,4 +30,11 @@ internal object NoOpTestModule {
@JvmStatic
@MockHttpInterceptor
fun providesTestInterceptor(): TestInterceptor? = null
+
+ @Provides
+ @JvmStatic
+ @MatrixScope
+ fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver {
+ return DefaultBackgroundDetectionObserver()
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt
index bafffdf852..7d004bc5c0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt
@@ -17,24 +17,38 @@
package org.matrix.android.sdk.internal.di
import android.content.Context
+import androidx.lifecycle.Observer
import androidx.work.Constraints
import androidx.work.ListenableWorker
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkRequest
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
+import org.matrix.android.sdk.internal.session.SessionScope
+import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import java.util.concurrent.TimeUnit
import javax.inject.Inject
+@SessionScope
internal class WorkManagerProvider @Inject constructor(
context: Context,
- @SessionId private val sessionId: String
+ @SessionId private val sessionId: String,
+ private val coroutineDispatchers: MatrixCoroutineDispatchers,
+ private val sessionScope: CoroutineScope
) {
private val tag = MATRIX_SDK_TAG_PREFIX + sessionId
val workManager = WorkManager.getInstance(context)
+ init {
+ checkIfWorkerFactoryIsSetup()
+ }
+
/**
* Create a OneTimeWorkRequestBuilder, with the Matrix SDK tag
*/
@@ -60,6 +74,27 @@ internal class WorkManagerProvider @Inject constructor(
}
}
+ private fun checkIfWorkerFactoryIsSetup() {
+ sessionScope.launch(coroutineDispatchers.main) {
+ val checkWorkerRequest = OneTimeWorkRequestBuilder().build()
+ workManager.enqueue(checkWorkerRequest)
+ val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id)
+ val observer = object : Observer {
+ override fun onChanged(workInfo: WorkInfo) {
+ if (workInfo.state.isFinished) {
+ checkWorkerLiveState.removeObserver(this)
+ if (workInfo.state == WorkInfo.State.FAILED) {
+ throw RuntimeException("MatrixWorkerFactory is not being set on your worker configuration.\n" +
+ "Makes sure to add it to a DelegatingWorkerFactory if you have your own factory or use it directly.\n" +
+ "You can grab the instance through the Matrix class.")
+ }
+ }
+ }
+ }
+ checkWorkerLiveState.observeForever(observer)
+ }
+ }
+
companion object {
private const val MATRIX_SDK_TAG_PREFIX = "MatrixSDK-"
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
index 9fc84e6fe5..a89713870a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt
@@ -203,8 +203,11 @@ internal class MxCallImpl(
override fun selectAnswer() {
Timber.tag(loggerTag.value).v("select answer $callId")
- if (isOutgoing) return
- state = CallState.Answering
+ if (!isOutgoing) return
+ // This is an outgoing call, select the remote client that answered.
+ if (state != CallState.Dialing && state !is CallState.Connected) {
+ Timber.tag(loggerTag.value).w("Expected state is CallState.Dialing or CallState.Connected got $state.")
+ }
CallSelectAnswerContent(
callId = callId,
partyId = ourPartyId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
index 74c5254cf5..77dcbed18e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
@@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.util.MimeTypes
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
@@ -63,8 +64,8 @@ private data class NewAttachmentAttributes(
* Possible previous worker: None
* Possible next worker : Always [MultipleEventSendingDispatcherWorker]
*/
-internal class UploadContentWorker(val context: Context, params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class UploadContentWorker(val context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt
index 338f43bdbb..716859f195 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt
@@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.group
import android.content.Context
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
@@ -28,8 +29,8 @@ import javax.inject.Inject
* Possible previous worker: None
* Possible next worker : None
*/
-internal class GetGroupDataWorker(context: Context, params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class GetGroupDataWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt
index 0ac21b555e..da15e158e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt
@@ -56,6 +56,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
val allEvents = (newJoinEvents + inviteEvents).filter { event ->
when (event.type) {
+ EventType.POLL_START,
EventType.MESSAGE,
EventType.REDACTION,
EventType.ENCRYPTED,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt
index 4df42b2cfb..ce29efaaac 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt
@@ -19,13 +19,14 @@ import android.content.Context
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject
-internal class AddPusherWorker(context: Context, params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class AddPusherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
index 5a1eb190a8..62b6d626f5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
@@ -17,20 +17,27 @@ package org.matrix.android.sdk.internal.session.room
import io.realm.Realm
import org.matrix.android.sdk.api.crypto.VerificationState
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.api.session.events.model.getRelationContent
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
+import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
import org.matrix.android.sdk.api.session.room.model.VoteInfo
+import org.matrix.android.sdk.api.session.room.model.VoteSummary
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
+import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import org.matrix.android.sdk.internal.crypto.verification.toState
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
@@ -50,11 +57,13 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
+import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import timber.log.Timber
import javax.inject.Inject
internal class EventRelationsAggregationProcessor @Inject constructor(
- @UserId private val userId: String
+ @UserId private val userId: String,
+ private val stateEventDataSource: StateEventDataSource
) : EventInsertLiveProcessor {
private val allowedTypes = listOf(
@@ -69,7 +78,9 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
// TODO Add ?
// EventType.KEY_VERIFICATION_READY,
EventType.KEY_VERIFICATION_KEY,
- EventType.ENCRYPTED
+ EventType.ENCRYPTED,
+ EventType.POLL_RESPONSE,
+ EventType.POLL_END
)
override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean {
@@ -107,9 +118,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
// A replace!
handleReplace(realm, event, content, roomId, isLocalEcho)
- } else if (content?.relatesTo?.type == RelationType.RESPONSE) {
- Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
- handleResponse(realm, event, content, roomId, isLocalEcho)
}
}
@@ -139,9 +147,11 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
// A replace!
handleReplace(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
- } else if (encryptedEventContent.relatesTo.type == RelationType.RESPONSE) {
- Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
- handleResponse(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
+ } else if (event.getClearType() == EventType.POLL_RESPONSE) {
+ event.getClearContent().toModel(catchError = true)?.let { pollResponseContent ->
+ Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
+ handleResponse(realm, event, pollResponseContent, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
+ }
}
}
} else if (encryptedEventContent?.relatesTo?.type == RelationType.REFERENCE) {
@@ -158,6 +168,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
handleVerification(realm, event, roomId, isLocalEcho, it)
}
}
+ EventType.POLL_RESPONSE -> {
+ event.getClearContent().toModel(catchError = true)?.let {
+ handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId)
+ }
+ }
+ EventType.POLL_END -> {
+ event.content.toModel(catchError = true)?.let {
+ handleEndPoll(realm, event, it, roomId, isLocalEcho)
+ }
+ }
}
} else if (encryptedEventContent?.relatesTo?.type == RelationType.ANNOTATION) {
// Reaction
@@ -188,6 +208,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
}
}
+ EventType.POLL_RESPONSE -> {
+ event.content.toModel(catchError = true)?.let {
+ handleResponse(realm, event, it, roomId, isLocalEcho)
+ }
+ }
+ EventType.POLL_END -> {
+ event.content.toModel(catchError = true)?.let {
+ handleEndPoll(realm, event, it, roomId, isLocalEcho)
+ }
+ }
else -> Timber.v("UnHandled event ${event.eventId}")
}
} catch (t: Throwable) {
@@ -276,7 +306,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
private fun handleResponse(realm: Realm,
event: Event,
- content: MessageContent,
+ content: MessagePollResponseContent,
roomId: String,
isLocalEcho: Boolean,
relatedEventId: String? = null) {
@@ -321,11 +351,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
return
}
- val responseContent = event.content.toModel() ?: return Unit.also {
- Timber.d("## POLL Receiving malformed response eventId:$eventId content: ${event.content}")
- }
-
- val optionIndex = responseContent.relatesTo?.option ?: return Unit.also {
+ val option = content.response?.answers?.first() ?: return Unit.also {
Timber.d("## POLL Ignoring malformed response no option eventId:$eventId content: ${event.content}")
}
@@ -336,22 +362,36 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
val existingVote = votes[existingVoteIndex]
if (existingVote.voteTimestamp < eventTimestamp) {
// Take the new one
- votes[existingVoteIndex] = VoteInfo(senderId, optionIndex, eventTimestamp)
+ votes[existingVoteIndex] = VoteInfo(senderId, option, eventTimestamp)
if (userId == senderId) {
- sumModel.myVote = optionIndex
+ sumModel.myVote = option
}
- Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$targetEventId ")
+ Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ")
} else {
Timber.v("## POLL Ignoring vote (older than known one) eventId:$eventId ")
}
} else {
- votes.add(VoteInfo(senderId, optionIndex, eventTimestamp))
+ votes.add(VoteInfo(senderId, option, eventTimestamp))
if (userId == senderId) {
- sumModel.myVote = optionIndex
+ sumModel.myVote = option
}
- Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$targetEventId ")
+ Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ")
}
sumModel.votes = votes
+
+ // Precompute the percentage of votes for all options
+ val totalVotes = votes.size
+ sumModel.totalVotes = totalVotes
+ sumModel.votesSummary = votes
+ .groupBy({ it.option }, { it.userId })
+ .mapValues {
+ VoteSummary(
+ total = it.value.size,
+ percentage = if (totalVotes == 0 && it.value.isEmpty()) 0.0 else it.value.size.toDouble() / totalVotes
+ )
+ }
+ sumModel.winnerVoteCount = sumModel.votesSummary?.maxOf { it.value.total } ?: 0
+
if (isLocalEcho) {
existingPollSummary.sourceLocalEchoEvents.add(eventId)
} else {
@@ -361,6 +401,51 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
existingPollSummary.aggregatedContent = ContentMapper.map(sumModel.toContent())
}
+ private fun handleEndPoll(realm: Realm,
+ event: Event,
+ content: MessageEndPollContent,
+ roomId: String,
+ isLocalEcho: Boolean) {
+ val pollEventId = content.relatesTo?.eventId ?: return
+
+ var existing = EventAnnotationsSummaryEntity.where(realm, roomId, pollEventId).findFirst()
+ if (existing == null) {
+ Timber.v("## POLL creating new relation summary for $pollEventId")
+ existing = EventAnnotationsSummaryEntity.create(realm, roomId, pollEventId)
+ }
+
+ // we have it
+ val existingPollSummary = existing.pollResponseSummary
+ ?: realm.createObject(PollResponseAggregatedSummaryEntity::class.java).also {
+ existing.pollResponseSummary = it
+ }
+
+ if (existingPollSummary.closedTime != null) {
+ Timber.v("## Received poll.end event for already ended poll $pollEventId")
+ return
+ }
+
+ val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
+ ?.content?.toModel()
+ ?.let { PowerLevelsHelper(it) }
+ if (!powerLevelsHelper?.isUserAbleToRedact(event.senderId ?: "").orFalse()) {
+ Timber.v("## Received poll.end event $pollEventId but user ${event.senderId} doesn't have enough power level in room $roomId")
+ return
+ }
+
+ val txId = event.unsignedData?.transactionId
+ // is it a remote echo?
+ if (!isLocalEcho && existingPollSummary.sourceLocalEchoEvents.contains(txId)) {
+ // ok it has already been managed
+ Timber.v("## POLL Receiving remote echo of response eventId:$pollEventId")
+ existingPollSummary.sourceLocalEchoEvents.remove(txId)
+ existingPollSummary.sourceEvents.add(event.eventId)
+ return
+ }
+
+ existingPollSummary.closedTime = event.originServerTs
+ }
+
private fun handleInitialAggregatedRelations(realm: Realm,
event: Event,
roomId: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
index 23b7767816..5ae4007c63 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
@@ -70,7 +70,8 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
} else {
when (typeToPrune) {
EventType.ENCRYPTED,
- EventType.MESSAGE -> {
+ EventType.MESSAGE,
+ EventType.POLL_START -> {
Timber.d("REDACTION for message ${eventToPrune.eventId}")
val unsignedData = EventMapper.map(eventToPrune).unsignedData
?: UnsignedData(null, null)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index 77aadef6bd..d3162aef79 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -103,8 +103,14 @@ internal class DefaultSendService @AssistedInject constructor(
.let { sendEvent(it) }
}
- override fun sendOptionsReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable {
- return localEchoEventFactory.createOptionsReplyEvent(roomId, pollEventId, optionIndex, optionValue)
+ override fun voteToPoll(pollEventId: String, answerId: String): Cancelable {
+ return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId)
+ .also { createLocalEcho(it) }
+ .let { sendEvent(it) }
+ }
+
+ override fun endPoll(pollEventId: String): Cancelable {
+ return localEchoEventFactory.createEndPollEvent(roomId, pollEventId)
.also { createLocalEcho(it) }
.let { sendEvent(it) }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
index c4630056fb..c1e77d7e60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
@@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.model.message.ImageInfo
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
+import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
@@ -46,6 +47,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.PollAnswer
import org.matrix.android.sdk.api.session.room.model.message.PollCreationInfo
import org.matrix.android.sdk.api.session.room.model.message.PollQuestion
+import org.matrix.android.sdk.api.session.room.model.message.PollResponse
import org.matrix.android.sdk.api.session.room.model.message.ThumbnailInfo
import org.matrix.android.sdk.api.session.room.model.message.VideoInfo
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
@@ -123,19 +125,28 @@ internal class LocalEchoEventFactory @Inject constructor(
))
}
- fun createOptionsReplyEvent(roomId: String,
- pollEventId: String,
- optionIndex: Int,
- optionLabel: String): Event {
- return createMessageEvent(roomId,
- MessagePollResponseContent(
- body = optionLabel,
- relatesTo = RelationDefaultContent(
- type = RelationType.RESPONSE,
- option = optionIndex,
- eventId = pollEventId)
+ fun createPollReplyEvent(roomId: String,
+ pollEventId: String,
+ answerId: String): Event {
+ val content = MessagePollResponseContent(
+ body = answerId,
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REFERENCE,
+ eventId = pollEventId),
+ response = PollResponse(
+ answers = listOf(answerId)
+ )
- ))
+ )
+ val localId = LocalEcho.createLocalEchoId()
+ return Event(
+ roomId = roomId,
+ originServerTs = dummyOriginServerTs(),
+ senderId = userId,
+ eventId = localId,
+ type = EventType.POLL_RESPONSE,
+ content = content.toContent(),
+ unsignedData = UnsignedData(age = null, transactionId = localId))
}
fun createPollEvent(roomId: String,
@@ -148,7 +159,7 @@ internal class LocalEchoEventFactory @Inject constructor(
),
answers = options.mapIndexed { index, option ->
PollAnswer(
- id = index.toString(),
+ id = "$index-$option",
answer = option
)
}
@@ -165,6 +176,25 @@ internal class LocalEchoEventFactory @Inject constructor(
unsignedData = UnsignedData(age = null, transactionId = localId))
}
+ fun createEndPollEvent(roomId: String,
+ eventId: String): Event {
+ val content = MessageEndPollContent(
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REFERENCE,
+ eventId = eventId
+ )
+ )
+ val localId = LocalEcho.createLocalEchoId()
+ return Event(
+ roomId = roomId,
+ originServerTs = dummyOriginServerTs(),
+ senderId = userId,
+ eventId = localId,
+ type = EventType.POLL_END,
+ content = content.toContent(),
+ unsignedData = UnsignedData(age = null, transactionId = localId))
+ }
+
fun createReplaceTextOfReply(roomId: String,
eventReplaced: TimelineEvent,
originalEvent: TimelineEvent,
@@ -418,7 +448,7 @@ internal class LocalEchoEventFactory @Inject constructor(
when (content?.msgType) {
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_TEXT,
- MessageType.MSGTYPE_NOTICE -> {
+ MessageType.MSGTYPE_NOTICE -> {
var formattedText: String? = null
if (content is MessageContentWithFormattedBody) {
formattedText = content.matrixFormattedBody
@@ -429,11 +459,12 @@ internal class LocalEchoEventFactory @Inject constructor(
TextContent(content.body, formattedText)
}
}
- MessageType.MSGTYPE_FILE -> return TextContent("sent a file.")
- MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
- MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
- MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
- else -> return TextContent(content?.body ?: "")
+ MessageType.MSGTYPE_FILE -> return TextContent("sent a file.")
+ MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
+ MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
+ MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
+ MessageType.MSGTYPE_POLL_START -> return TextContent((content as? MessagePollContent)?.pollCreationInfo?.question?.question ?: "")
+ else -> return TextContent(content?.body ?: "")
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt
index 16a9eba363..f44c255f1e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt
@@ -21,6 +21,7 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
@@ -38,8 +39,8 @@ import javax.inject.Inject
* Possible previous worker: Always [UploadContentWorker]
* Possible next worker : None, but it will post new work to send events, encrypted or not
*/
-internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt
index b4436bfcbf..c03d1fa81e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt
@@ -19,6 +19,7 @@ import android.content.Context
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.SessionComponent
@@ -32,8 +33,8 @@ import javax.inject.Inject
* Possible previous worker: None
* Possible next worker : None
*/
-internal class RedactEventWorker(context: Context, params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class RedactEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt
index 8b7fe4b907..7f24688ece 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt
@@ -23,6 +23,7 @@ import io.realm.RealmConfiguration
import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.tasks.SendEventTask
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionComponent
@@ -38,9 +39,8 @@ import javax.inject.Inject
* Possible previous worker: [EncryptEventWorker] or first worker
* Possible next worker : None
*/
-internal class SendEventWorker(context: Context,
- params: WorkerParameters) :
- SessionSafeCoroutineWorker(context, params, Params::class.java) {
+internal class SendEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt
index 35e561a106..869a4d425a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt
@@ -25,9 +25,12 @@ import javax.inject.Inject
internal class SyncTokenStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
fun getLastToken(): String? {
- return Realm.getInstance(monarchy.realmConfiguration).use {
+ val token = Realm.getInstance(monarchy.realmConfiguration).use {
+ // Makes sure realm is up-to-date as it's used for querying internally on non looper thread.
+ it.refresh()
it.where(SyncEntity::class.java).findFirst()?.nextBatch
}
+ return token
}
fun saveToken(realm: Realm, token: String?) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt
index 41bb1a44a6..763cd55714 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt
@@ -21,8 +21,8 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.isTokenError
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.di.WorkManagerProvider
-import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.sync.SyncPresence
import org.matrix.android.sdk.internal.session.sync.SyncTask
@@ -41,9 +41,8 @@ private const val DEFAULT_DELAY_TIMEOUT = 30_000L
* Possible previous worker: None
* Possible next worker : None
*/
-internal class SyncWorker(context: Context,
- workerParameters: WorkerParameters
-) : SessionSafeCoroutineWorker(context, workerParameters, Params::class.java) {
+internal class SyncWorker(context: Context, workerParameters: WorkerParameters, sessionManager: SessionManager) :
+ SessionSafeCoroutineWorker(context, workerParameters, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
@@ -56,7 +55,6 @@ internal class SyncWorker(context: Context,
@Inject lateinit var syncTask: SyncTask
@Inject lateinit var taskExecutor: TaskExecutor
- @Inject lateinit var networkConnectivityChecker: NetworkConnectivityChecker
@Inject lateinit var workManagerProvider: WorkManagerProvider
override fun injectWith(injector: SessionComponent) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt
index d40fd8d076..c52c6a404e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt
@@ -18,10 +18,13 @@ package org.matrix.android.sdk.internal.session.terms
import dagger.Lazy
import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
import org.matrix.android.sdk.api.session.terms.TermsService
+import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.network.RetrofitFactory
@@ -55,6 +58,27 @@ internal class DefaultTermsService @Inject constructor(
return GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData())
}
+ /**
+ * We use a trick here to get the homeserver T&C, we use the register API
+ */
+ override suspend fun getHomeserverTerms(baseUrl: String): TermsResponse {
+ return try {
+ executeRequest(null) {
+ termsAPI.register(baseUrl + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
+ }
+ // Return empty result if it succeed, but it should never happen
+ TermsResponse()
+ } catch (throwable: Throwable) {
+ @Suppress("UNCHECKED_CAST")
+ TermsResponse(
+ policies = (throwable.toRegistrationFlowResponse()
+ ?.params
+ ?.get(LoginFlowTypes.TERMS) as? JsonDict)
+ ?.get("policies") as? JsonDict
+ )
+ }
+ }
+
override suspend fun agreeToTerms(serviceType: TermsService.ServiceType,
baseUrl: String,
agreedUrls: List,
@@ -91,7 +115,7 @@ internal class DefaultTermsService @Inject constructor(
private fun buildUrl(baseUrl: String, serviceType: TermsService.ServiceType): String {
val servicePath = when (serviceType) {
TermsService.ServiceType.IntegrationManager -> NetworkConstants.URI_INTEGRATION_MANAGER_PATH
- TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2
+ TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2
}
return "${baseUrl.ensureTrailingSlash()}$servicePath"
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt
index 91d27030de..fb6aff5a9e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt
@@ -16,6 +16,8 @@
package org.matrix.android.sdk.internal.session.terms
+import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.api.util.emptyJsonDict
import org.matrix.android.sdk.internal.network.HttpHeaders
import retrofit2.http.Body
import retrofit2.http.GET
@@ -37,4 +39,12 @@ internal interface TermsAPI {
suspend fun agreeToTerms(@Url url: String,
@Body params: AcceptTermsBody,
@Header(HttpHeaders.Authorization) token: String)
+
+ /**
+ * API to retrieve the terms for a homeserver. The API /terms does not exist yet, so retrieve the terms from the login flow.
+ * We do not care about the result (Credentials)
+ */
+ @POST
+ suspend fun register(@Url url: String,
+ @Body body: JsonDict = emptyJsonDict)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
index 3e977b31fb..9c8b36a3ed 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
@@ -18,26 +18,32 @@ package org.matrix.android.sdk.internal.util
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
-import org.matrix.android.sdk.internal.di.MatrixScope
import timber.log.Timber
-import javax.inject.Inject
-/**
- * To be attached to ProcessLifecycleOwner lifecycle
- */
-@MatrixScope
-internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecycleObserver {
+interface BackgroundDetectionObserver : DefaultLifecycleObserver {
+ val isInBackground: Boolean
- var isInBackground: Boolean = true
+ fun register(listener: Listener)
+ fun unregister(listener: Listener)
+
+ interface Listener {
+ fun onMoveToForeground()
+ fun onMoveToBackground()
+ }
+}
+
+internal class DefaultBackgroundDetectionObserver : BackgroundDetectionObserver {
+
+ override var isInBackground: Boolean = true
private set
- private val listeners = LinkedHashSet()
+ private val listeners = LinkedHashSet()
- fun register(listener: Listener) {
+ override fun register(listener: BackgroundDetectionObserver.Listener) {
listeners.add(listener)
}
- fun unregister(listener: Listener) {
+ override fun unregister(listener: BackgroundDetectionObserver.Listener) {
listeners.remove(listener)
}
@@ -52,9 +58,4 @@ internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecy
isInBackground = true
listeners.forEach { it.onMoveToBackground() }
}
-
- interface Listener {
- fun onMoveToForeground()
- fun onMoveToBackground()
- }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt
index b58cab99b5..0b451e9c34 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt
@@ -17,16 +17,33 @@
package org.matrix.android.sdk.internal.worker
import android.content.Context
+import androidx.work.CoroutineWorker
import androidx.work.ListenableWorker
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
+import org.matrix.android.sdk.internal.SessionManager
+import org.matrix.android.sdk.internal.crypto.CancelGossipRequestWorker
+import org.matrix.android.sdk.internal.crypto.SendGossipRequestWorker
+import org.matrix.android.sdk.internal.crypto.SendGossipWorker
+import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker
+import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker
+import org.matrix.android.sdk.internal.di.MatrixScope
+import org.matrix.android.sdk.internal.session.content.UploadContentWorker
+import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker
+import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
+import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
+import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker
+import org.matrix.android.sdk.internal.session.room.send.SendEventWorker
+import org.matrix.android.sdk.internal.session.sync.job.SyncWorker
import timber.log.Timber
import javax.inject.Inject
-import javax.inject.Provider
-class MatrixWorkerFactory @Inject constructor(
- private val workerFactories: Map, @JvmSuppressWildcards Provider>
-) : WorkerFactory() {
+/**
+ * This factory is responsible of creating Workers by giving the session manager.
+ * This is not the cleanest way but getting SessionComponent is dependant of args type.
+ */
+@MatrixScope
+internal class MatrixWorkerFactory @Inject constructor(private val sessionManager: SessionManager) : WorkerFactory() {
override fun createWorker(
appContext: Context,
@@ -34,11 +51,61 @@ class MatrixWorkerFactory @Inject constructor(
workerParameters: WorkerParameters
): ListenableWorker? {
Timber.d("MatrixWorkerFactory.createWorker for $workerClassName")
+ return when (workerClassName) {
+ CheckFactoryWorker::class.java.name ->
+ CheckFactoryWorker(appContext, workerParameters, true)
+ AddPusherWorker::class.java.name ->
+ AddPusherWorker(appContext, workerParameters, sessionManager)
+ CancelGossipRequestWorker::class.java.name ->
+ CancelGossipRequestWorker(appContext, workerParameters, sessionManager)
+ GetGroupDataWorker::class.java.name ->
+ GetGroupDataWorker(appContext, workerParameters, sessionManager)
+ MultipleEventSendingDispatcherWorker::class.java.name ->
+ MultipleEventSendingDispatcherWorker(appContext, workerParameters, sessionManager)
+ RedactEventWorker::class.java.name ->
+ RedactEventWorker(appContext, workerParameters, sessionManager)
+ SendEventWorker::class.java.name ->
+ SendEventWorker(appContext, workerParameters, sessionManager)
+ SendGossipRequestWorker::class.java.name ->
+ SendGossipRequestWorker(appContext, workerParameters, sessionManager)
+ SendGossipWorker::class.java.name ->
+ SendGossipWorker(appContext, workerParameters, sessionManager)
+ SendVerificationMessageWorker::class.java.name ->
+ SendVerificationMessageWorker(appContext, workerParameters, sessionManager)
+ SyncWorker::class.java.name ->
+ SyncWorker(appContext, workerParameters, sessionManager)
+ UpdateTrustWorker::class.java.name ->
+ UpdateTrustWorker(appContext, workerParameters, sessionManager)
+ UploadContentWorker::class.java.name ->
+ UploadContentWorker(appContext, workerParameters, sessionManager)
+ else -> {
+ Timber.w("No worker defined on MatrixWorkerFactory for $workerClassName will delegate to default.")
+ // Return null to delegate to the default WorkerFactory.
+ null
+ }
+ }
+ }
- val foundEntry =
- workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
- val factoryProvider = foundEntry?.value
- ?: throw IllegalArgumentException("unknown worker class name: $workerClassName")
- return factoryProvider.get().create(appContext, workerParameters)
+ /**
+ * This worker is launched by the factory with the isCreatedByMatrixWorkerFactory flag to true.
+ * If the MatrixWorkerFactory is not set up, it will default to the other constructor and it will throw
+ */
+ class CheckFactoryWorker(context: Context,
+ workerParameters: WorkerParameters,
+ private val isCreatedByMatrixWorkerFactory: Boolean) :
+ CoroutineWorker(context, workerParameters) {
+
+ // Called by WorkManager if there is no MatrixWorkerFactory
+ constructor(context: Context, workerParameters: WorkerParameters) : this(context,
+ workerParameters,
+ isCreatedByMatrixWorkerFactory = false)
+
+ override suspend fun doWork(): Result {
+ return if (!isCreatedByMatrixWorkerFactory) {
+ Result.failure()
+ } else {
+ Result.success()
+ }
+ }
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt
index d4179e2272..334c4580e9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt
@@ -22,6 +22,7 @@ import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.session.SessionComponent
import timber.log.Timber
@@ -33,6 +34,7 @@ import timber.log.Timber
internal abstract class SessionSafeCoroutineWorker(
context: Context,
workerParameters: WorkerParameters,
+ private val sessionManager: SessionManager,
private val paramClass: Class
) : CoroutineWorker(context, workerParameters) {
@@ -48,7 +50,7 @@ internal abstract class SessionSafeCoroutineWorker(
.also { Timber.e("Unable to parse work parameters") }
return try {
- val sessionComponent = getSessionComponent(params.sessionId)
+ val sessionComponent = sessionManager.getSessionComponent(params.sessionId)
?: return buildErrorResult(params, "No session")
// Make sure to inject before handling error as you may need some dependencies to process them.
diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt
index b135954f63..6ca86be095 100644
--- a/tools/check/forbidden_strings_in_code.txt
+++ b/tools/check/forbidden_strings_in_code.txt
@@ -160,7 +160,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
-enum class===108
+enum class===114
### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
diff --git a/tools/check/forbidden_strings_in_layout.txt b/tools/check/forbidden_strings_in_layout.txt
index 09f0d01f69..545983f844 100644
--- a/tools/check/forbidden_strings_in_layout.txt
+++ b/tools/check/forbidden_strings_in_layout.txt
@@ -25,3 +25,8 @@
### Use style="@style/Widget.Vector.TextView.*" instead of textSize attribute
android:textSize===9
+
+### Use `@id` and not `@+id` when referencing ids in layouts
+layout_(.*)="@\+id
+accessibilityTraversal(.*)="@\+id
+toolbarId="@\+id
diff --git a/tools/compressVideo.sh b/tools/compressVideo.sh
new file mode 100755
index 0000000000..5f9de9820b
--- /dev/null
+++ b/tools/compressVideo.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+#
+# Copyright (c) 2021 New Vector Ltd
+#
+# 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.
+#
+
+set -e
+echo "Converting file $1"
+file=$(echo $1 | sed 's/\.[^.]*$//')
+ffmpeg -i $1 -filter_complex "[0:v] fps=12,scale=480:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" $file-tmp.gif
+echo "Converting to Gif"
+gifsicle -O3 --lossy=80 -o $file.gif $file-tmp.gif
+rm $file-tmp.gif
+echo "Done, $file.gif has been generated"
diff --git a/tools/import_analytic_plan.sh b/tools/import_analytic_plan.sh
new file mode 100755
index 0000000000..9c020a8e37
--- /dev/null
+++ b/tools/import_analytic_plan.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+echo "Deleted existing plan..."
+rm vector/src/main/java/im/vector/app/features/analytics/plan/*.*
+
+echo "Cloning analytics project..."
+mkdir analytics_tmp
+cd analytics_tmp
+git clone https://github.com/matrix-org/matrix-analytics-events.git
+
+echo "Copy plan..."
+cp matrix-analytics-events/types/kotlin2/* ../vector/src/main/java/im/vector/app/features/analytics/plan/
+
+echo "Cleanup."
+cd ..
+rm -rf analytics_tmp
+
+echo "Done."
diff --git a/tools/release/sign_apk.sh b/tools/release/sign_apk.sh
index aae9e1a378..de5a22dd34 100755
--- a/tools/release/sign_apk.sh
+++ b/tools/release/sign_apk.sh
@@ -17,7 +17,7 @@ PARAM_KEYSTORE_PATH=$1
PARAM_APK=$2
# Other params
-BUILD_TOOLS_VERSION="31.0.0-rc5"
+BUILD_TOOLS_VERSION="31.0.0"
MIN_SDK_VERSION=21
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."
diff --git a/tools/release/sign_apk_unsafe.sh b/tools/release/sign_apk_unsafe.sh
index 5d209a4a2b..a7536616e9 100755
--- a/tools/release/sign_apk_unsafe.sh
+++ b/tools/release/sign_apk_unsafe.sh
@@ -23,7 +23,7 @@ PARAM_KS_PASS=$3
PARAM_KEY_PASS=$4
# Other params
-BUILD_TOOLS_VERSION="31.0.0-rc5"
+BUILD_TOOLS_VERSION="31.0.0"
MIN_SDK_VERSION=21
echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..."
diff --git a/tools/templates/ElementFeature/root/src/app_package/Activity.kt.ftl b/tools/templates/ElementFeature/root/src/app_package/Activity.kt.ftl
index a4622d1d21..a5c097065e 100644
--- a/tools/templates/ElementFeature/root/src/app_package/Activity.kt.ftl
+++ b/tools/templates/ElementFeature/root/src/app_package/Activity.kt.ftl
@@ -35,9 +35,9 @@ class ${activityClass} : VectorBaseActivity(), ToolbarConfigurable {
<#if createFragmentArgs>
val fragmentArgs: ${fragmentArgsClass} = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS)
?: return
- addFragment(R.id.simpleFragmentContainer, ${fragmentClass}::class.java, fragmentArgs)
+ addFragment(views.simpleFragmentContainer.id, ${fragmentClass}::class.java, fragmentArgs)
<#else>
- addFragment(R.id.simpleFragmentContainer, ${fragmentClass}::class.java)
+ addFragment(views.simpleFragmentContainer.id, ${fragmentClass}::class.java)
#if>
}
}
diff --git a/tools/templates/ElementFeature/template.xml b/tools/templates/ElementFeature/template.xml
index 14c718c993..71cf74f19b 100644
--- a/tools/templates/ElementFeature/template.xml
+++ b/tools/templates/ElementFeature/template.xml
@@ -105,8 +105,6 @@
suggest="${underscoreToCamelCase(classToResource(fragmentClass))}ViewEvents"
default="MainViewEvents"
help="The name of the view events to create" />
-
-
+
+
diff --git a/vector/src/debug/java/im/vector/app/config/AnalyticsConfig.kt b/vector/src/debug/java/im/vector/app/config/AnalyticsConfig.kt
new file mode 100644
index 0000000000..34f2d4f92b
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/config/AnalyticsConfig.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.config
+
+import im.vector.app.BuildConfig
+import im.vector.app.features.analytics.AnalyticsConfig
+
+val analyticsConfig: AnalyticsConfig = object : AnalyticsConfig {
+ override val isEnabled = BuildConfig.APPLICATION_ID == "im.vector.app.debug"
+ override val postHogHost = "https://posthog-poc.lab.element.dev"
+ override val postHogApiKey = "rs-pJjsYJTuAkXJfhaMmPUNBhWliDyTKLOOxike6ck8"
+ override val policyLink = "https://element.io/cookie-policy"
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
index 64de648a23..a2b2b44ce3 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
@@ -34,6 +34,8 @@ import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityDebugMenuBinding
+import im.vector.app.features.debug.analytics.DebugAnalyticsActivity
+import im.vector.app.features.debug.features.DebugFeaturesSettingsActivity
import im.vector.app.features.debug.sas.DebugSasEmojiActivity
import im.vector.app.features.debug.settings.DebugPrivateSettingsActivity
import im.vector.app.features.qrcode.QrCodeScannerActivity
@@ -76,7 +78,11 @@ class DebugMenuActivity : VectorBaseActivity() {
}
private fun setupViews() {
+ views.debugFeatures.setOnClickListener { startActivity(Intent(this, DebugFeaturesSettingsActivity::class.java)) }
views.debugPrivateSetting.setOnClickListener { openPrivateSettings() }
+ views.debugAnalytics.setOnClickListener {
+ startActivity(Intent(this, DebugAnalyticsActivity::class.java))
+ }
views.debugTestTextViewLink.setOnClickListener { testTextViewLink() }
views.debugOpenButtonStylesLight.setOnClickListener {
startActivity(Intent(this, DebugVectorButtonStylesLightActivity::class.java))
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt
new file mode 100644
index 0000000000..61883251ce
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.analytics
+
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.databinding.ActivitySimpleBinding
+
+@AndroidEntryPoint
+class DebugAnalyticsActivity : VectorBaseActivity() {
+
+ override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
+
+ override fun initUiAndData() {
+ if (isFirstCreation()) {
+ addFragment(
+ views.simpleFragmentContainer,
+ DebugAnalyticsFragment::class.java
+ )
+ }
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
new file mode 100644
index 0000000000..eb23fe6383
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.analytics
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.app.core.epoxy.onClick
+import im.vector.app.core.extensions.toOnOff
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentDebugAnalyticsBinding
+import me.gujun.android.span.span
+
+class DebugAnalyticsFragment : VectorBaseFragment() {
+
+ private val viewModel: DebugAnalyticsViewModel by fragmentViewModel()
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDebugAnalyticsBinding {
+ return FragmentDebugAnalyticsBinding.inflate(inflater, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setViewListeners()
+ }
+
+ private fun setViewListeners() {
+ views.showAnalyticsOptIn.onClick {
+ navigator.openAnalyticsOptIn(requireContext())
+ }
+ views.resetAnalyticsOptInDisplayed.onClick {
+ viewModel.handle(DebugAnalyticsViewActions.ResetAnalyticsOptInDisplayed)
+ }
+ }
+
+ override fun invalidate() = withState(viewModel) { state ->
+ views.analyticsStoreContent.text = span {
+ +"AnalyticsId: "
+ span {
+ textStyle = "bold"
+ text = state.analyticsId.orEmpty()
+ }
+ +"\nOptIn: "
+ span {
+ textStyle = "bold"
+ text = state.userConsent.toOnOff()
+ }
+ +"\nDidAsk: "
+ span {
+ textStyle = "bold"
+ text = state.didAskUserConsent.toString()
+ }
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt
similarity index 64%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt
rename to vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt
index 68e1953eff..e1a7ce36fd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-package org.matrix.android.sdk.internal.worker
+package im.vector.app.features.debug.analytics
-import android.content.Context
-import androidx.work.ListenableWorker
-import androidx.work.WorkerParameters
+import im.vector.app.core.platform.VectorViewModelAction
-interface DelegateWorkerFactory {
-
- fun create(context: Context, params: WorkerParameters): ListenableWorker
+sealed interface DebugAnalyticsViewActions : VectorViewModelAction {
+ object ResetAnalyticsOptInDisplayed : DebugAnalyticsViewActions
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt
new file mode 100644
index 0000000000..03e416813a
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.analytics
+
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.di.MavericksAssistedViewModelFactory
+import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.extensions.exhaustive
+import im.vector.app.core.platform.EmptyViewEvents
+import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.features.analytics.store.AnalyticsStore
+import kotlinx.coroutines.launch
+
+class DebugAnalyticsViewModel @AssistedInject constructor(
+ @Assisted initialState: DebugAnalyticsViewState,
+ private val analyticsStore: AnalyticsStore
+) : VectorViewModel(initialState) {
+
+ @AssistedFactory
+ interface Factory : MavericksAssistedViewModelFactory {
+ override fun create(initialState: DebugAnalyticsViewState): DebugAnalyticsViewModel
+ }
+
+ companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
+
+ init {
+ observerStore()
+ }
+
+ private fun observerStore() {
+ analyticsStore.analyticsIdFlow.setOnEach { copy(analyticsId = it) }
+ analyticsStore.userConsentFlow.setOnEach { copy(userConsent = it) }
+ analyticsStore.didAskUserConsentFlow.setOnEach { copy(didAskUserConsent = it) }
+ }
+
+ override fun handle(action: DebugAnalyticsViewActions) {
+ when (action) {
+ DebugAnalyticsViewActions.ResetAnalyticsOptInDisplayed -> handleResetAnalyticsOptInDisplayed()
+ }.exhaustive
+ }
+
+ private fun handleResetAnalyticsOptInDisplayed() {
+ viewModelScope.launch {
+ analyticsStore.setDidAskUserConsent(false)
+ }
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt
new file mode 100644
index 0000000000..8e7afb39ef
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.analytics
+
+import com.airbnb.mvrx.MavericksState
+
+data class DebugAnalyticsViewState(
+ val analyticsId: String? = null,
+ val userConsent: Boolean = false,
+ val didAskUserConsent: Boolean = false
+) : MavericksState
diff --git a/vector/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt b/vector/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt
new file mode 100644
index 0000000000..0c4a3ef637
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.di
+
+import android.content.Context
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import im.vector.app.features.DefaultVectorFeatures
+import im.vector.app.features.VectorFeatures
+import im.vector.app.features.debug.features.DebugVectorFeatures
+
+@InstallIn(SingletonComponent::class)
+@Module
+interface FeaturesModule {
+
+ @Binds
+ fun bindFeatures(debugFeatures: DebugVectorFeatures): VectorFeatures
+
+ companion object {
+
+ @Provides
+ fun providesDefaultVectorFeatures(): DefaultVectorFeatures {
+ return DefaultVectorFeatures()
+ }
+
+ @Provides
+ fun providesDebugVectorFeatures(context: Context, defaultVectorFeatures: DefaultVectorFeatures): DebugVectorFeatures {
+ return DebugVectorFeatures(context, defaultVectorFeatures)
+ }
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt b/vector/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt
index 8be4470b3f..6ef7fe441a 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt
@@ -23,12 +23,18 @@ import dagger.multibindings.IntoMap
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.MavericksViewModelComponent
import im.vector.app.core.di.MavericksViewModelKey
+import im.vector.app.features.debug.analytics.DebugAnalyticsViewModel
import im.vector.app.features.debug.settings.DebugPrivateSettingsViewModel
@InstallIn(MavericksViewModelComponent::class)
@Module
interface MavericksViewModelDebugModule {
+ @Binds
+ @IntoMap
+ @MavericksViewModelKey(DebugAnalyticsViewModel::class)
+ fun debugAnalyticsViewModelFactory(factory: DebugAnalyticsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
+
@Binds
@IntoMap
@MavericksViewModelKey(DebugPrivateSettingsViewModel::class)
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt
new file mode 100644
index 0000000000..e31f073614
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.features
+
+import android.os.Bundle
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.cleanup
+import im.vector.app.core.extensions.configureWith
+import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.databinding.FragmentGenericRecyclerBinding
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class DebugFeaturesSettingsActivity : VectorBaseActivity() {
+
+ @Inject lateinit var debugFeatures: DebugVectorFeatures
+ @Inject lateinit var debugFeaturesStateFactory: DebugFeaturesStateFactory
+ @Inject lateinit var controller: FeaturesController
+
+ override fun getBinding() = FragmentGenericRecyclerBinding.inflate(layoutInflater)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ controller.listener = object : EnumFeatureItem.Listener {
+ override fun > onOptionSelected(option: T?, feature: Feature.EnumFeature) {
+ debugFeatures.overrideEnum(option, feature.type)
+ }
+ }
+ views.genericRecyclerView.configureWith(controller)
+ controller.setData(debugFeaturesStateFactory.create())
+ }
+
+ override fun onDestroy() {
+ controller.listener = null
+ views.genericRecyclerView.cleanup()
+ super.onDestroy()
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
new file mode 100644
index 0000000000..8d22fc599f
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.features
+
+import im.vector.app.features.DefaultVectorFeatures
+import javax.inject.Inject
+
+class DebugFeaturesStateFactory @Inject constructor(
+ private val debugFeatures: DebugVectorFeatures,
+ private val defaultFeatures: DefaultVectorFeatures
+) {
+
+ fun create(): FeaturesState {
+ return FeaturesState(listOf(
+ createEnumFeature(
+ label = "Login version",
+ selection = debugFeatures.loginVersion(),
+ default = defaultFeatures.loginVersion()
+ )
+ ))
+ }
+
+ private inline fun > createEnumFeature(label: String, selection: T, default: T): Feature {
+ return Feature.EnumFeature(
+ label = label,
+ selection = selection.takeIf { debugFeatures.hasEnumOverride(T::class) },
+ default = default,
+ options = enumValues().toList(),
+ type = T::class
+ )
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
new file mode 100644
index 0000000000..0831609e4f
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.features
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.MutablePreferences
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import im.vector.app.features.DefaultVectorFeatures
+import im.vector.app.features.VectorFeatures
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
+import kotlin.reflect.KClass
+
+private val Context.dataStore: DataStore by preferencesDataStore(name = "debug_features")
+
+class DebugVectorFeatures(
+ context: Context,
+ private val vectorFeatures: DefaultVectorFeatures
+) : VectorFeatures {
+
+ private val dataStore = context.dataStore
+
+ override fun loginVersion(): VectorFeatures.LoginVersion {
+ return readPreferences().getEnum() ?: vectorFeatures.loginVersion()
+ }
+
+ fun > hasEnumOverride(type: KClass) = readPreferences().containsEnum(type)
+
+ fun > overrideEnum(value: T?, type: KClass) {
+ if (value == null) {
+ updatePreferences { it.removeEnum(type) }
+ } else {
+ updatePreferences { it.putEnum(value, type) }
+ }
+ }
+
+ private fun readPreferences() = runBlocking { dataStore.data.first() }
+
+ private fun updatePreferences(block: (MutablePreferences) -> Unit) = runBlocking {
+ dataStore.edit { block(it) }
+ }
+}
+
+private fun > MutablePreferences.removeEnum(type: KClass) {
+ remove(enumPreferencesKey(type))
+}
+
+private fun > Preferences.containsEnum(type: KClass) = contains(enumPreferencesKey(type))
+
+private fun > MutablePreferences.putEnum(value: T, type: KClass) {
+ this[enumPreferencesKey(type)] = value.name
+}
+
+private inline fun > Preferences.getEnum(): T? {
+ return get(enumPreferencesKey())?.let { enumValueOf(it) }
+}
+
+private inline fun > enumPreferencesKey() = enumPreferencesKey(T::class)
+
+private fun > enumPreferencesKey(type: KClass) = stringPreferencesKey("enum-${type.simpleName}")
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt b/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
new file mode 100644
index 0000000000..5dd2f9efa9
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.features
+
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import android.widget.TextView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.core.epoxy.VectorEpoxyHolder
+import im.vector.app.core.epoxy.VectorEpoxyModel
+
+@EpoxyModelClass(layout = im.vector.app.R.layout.item_feature)
+abstract class EnumFeatureItem : VectorEpoxyModel() {
+
+ @EpoxyAttribute
+ lateinit var feature: Feature.EnumFeature<*>
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ var listener: Listener? = null
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.label.text = feature.label
+
+ holder.optionsSpinner.apply {
+ val arrayAdapter = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item)
+ arrayAdapter.add("DEFAULT - ${feature.default.name}")
+ arrayAdapter.addAll(feature.options.map { it.name })
+ adapter = arrayAdapter
+
+ feature.selection?.let {
+ setSelection(feature.options.indexOf(it) + 1, false)
+ }
+
+ onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ when (position) {
+ 0 -> listener?.onOptionSelected(option = null, feature)
+ else -> feature.onOptionSelected(position - 1)
+ }
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ private fun > Feature.EnumFeature.onOptionSelected(selection: Int) {
+ listener?.onOptionSelected(options[selection], this)
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val label by bind(im.vector.app.R.id.feature_label)
+ val optionsSpinner by bind(im.vector.app.R.id.feature_options)
+ }
+
+ interface Listener {
+ fun > onOptionSelected(option: T?, feature: Feature.EnumFeature)
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt b/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt
new file mode 100644
index 0000000000..0a05c76d69
--- /dev/null
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * 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 im.vector.app.features.debug.features
+
+import com.airbnb.epoxy.TypedEpoxyController
+import javax.inject.Inject
+import kotlin.reflect.KClass
+
+data class FeaturesState(
+ val features: List
+)
+
+sealed interface Feature {
+
+ data class EnumFeature>(
+ val label: String,
+ val selection: T?,
+ val default: T,
+ val options: List,
+ val type: KClass
+ ) : Feature
+}
+
+class FeaturesController @Inject constructor() : TypedEpoxyController() {
+
+ var listener: EnumFeatureItem.Listener? = null
+
+ override fun buildModels(data: FeaturesState?) {
+ if (data == null) return
+
+ data.features.forEachIndexed { index, feature ->
+ when (feature) {
+ is Feature.EnumFeature<*> -> enumFeatureItem {
+ id(index)
+ feature(feature)
+ listener(this@FeaturesController.listener)
+ }
+ }
+ }
+ }
+}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt
index 25a068e794..a28394e57d 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt
@@ -17,7 +17,6 @@
package im.vector.app.features.debug.settings
import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.R
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
@@ -30,7 +29,7 @@ class DebugPrivateSettingsActivity : VectorBaseActivity()
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(
- R.id.simpleFragmentContainer,
+ views.simpleFragmentContainer,
DebugPrivateSettingsFragment::class.java
)
}
diff --git a/vector/src/debug/res/layout/activity_debug_menu.xml b/vector/src/debug/res/layout/activity_debug_menu.xml
index ac70e4ef0e..8b38c17b35 100644
--- a/vector/src/debug/res/layout/activity_debug_menu.xml
+++ b/vector/src/debug/res/layout/activity_debug_menu.xml
@@ -20,12 +20,24 @@
android:padding="@dimen/layout_horizontal_margin"
android:showDividers="middle">
+
+
+
+