Merge tag 'v1.3.0' into sc
Change-Id: Ib681fa5493f078b15d6110262ba622b9d0384d68 Conflicts: gradle.properties vector/build.gradle vector/src/main/java/im/vector/app/AppStateHandler.kt vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt vector/src/main/java/im/vector/app/core/pushers/VectorMessagingReceiver.kt vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt vector/src/main/res/layout/reaction_button.xml
This commit is contained in:
commit
4f93eb041c
|
@ -36,6 +36,7 @@ body:
|
|||
- [ ] Push `main` and the new tag `v1.1.10` to origin
|
||||
- [ ] Checkout `develop`
|
||||
- [ ] Increase version in `./vector/build.gradle`
|
||||
- [ ] Change the value of SDK_VERSION in the file `./matrix-sdk-android/build.gradle`
|
||||
- [ ] Commit and push `develop`
|
||||
- [ ] Wait for [Buildkite](https://buildkite.com/matrix-dot-org/element-android/builds?branch=main) to build the `main` branch.
|
||||
- [ ] Run the script `~/scripts/releaseElement.sh`. It will download the APKs from Buildkite check them and sign them.
|
||||
|
@ -72,9 +73,25 @@ body:
|
|||
- [ ] Create a release with GitFlow
|
||||
- [ ] Update the files `./build.gradle` and `./gradle/gradle-wrapper.properties` manually, to use the latest version for the dependency. You can get inspired by the same files on Element Android project.
|
||||
- [ ] Run the script `./tools/import_from_element.sh`
|
||||
- [ ] Update the version in `./matrix-sdk-android/build.gradle` and let the script finish to build the library
|
||||
- [ ] Update the version in `./matrix-sdk-android/build.gradle`
|
||||
- [ ] Check the diff on this file and restore what may have been erased (in particular the line `apply plugin: "com.vanniktech.maven.publish"`)
|
||||
- [ ] Let the script finish to build the library
|
||||
- [ ] Update the file `CHANGES.md`
|
||||
- [ ] Update the value of VERSION_NAME in the file gradle.properties
|
||||
- [ ] Finish the release using GitFlow
|
||||
|
||||
##### Release on MavenCentral
|
||||
|
||||
- [ ] Run the command `./gradlew publish --no-daemon --no-parallel`. You'll need some non-public element to do so
|
||||
- [ ] Connect to https://s01.oss.sonatype.org
|
||||
- [ ] Click on Staging Repositories and check the the files have been uploaded
|
||||
- [ ] Click on close
|
||||
- [ ] Wait (check Activity tab until step "Repository closed" is displayed)
|
||||
- [ ] Click on release. The staging repository will disappear
|
||||
- [ ] Check that the release is available in https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/ (it can take a few minutes)
|
||||
|
||||
##### Release on GitHub
|
||||
|
||||
- [ ] Create the release on GitHub from [the tag](https://github.com/matrix-org/matrix-android-sdk2/tags)
|
||||
- [ ] Upload the AAR on the GitHub release
|
||||
|
||||
|
@ -82,7 +99,7 @@ body:
|
|||
|
||||
https://github.com/matrix-org/matrix-android-sdk2-sample
|
||||
|
||||
- [ ] Update the dependency to the new version of the SDK2. Jitpack will have to build the AAR, it can take a few minutes. You can check status on https://jitpack.io/#matrix-org/matrix-android-sdk2
|
||||
- [ ] Update the dependency to the new version of the SDK2. It can take some time for MavenCentral to make the librarie available. You can check status on https://repo1.maven.org/maven2/org/matrix/android/matrix-android-sdk2/
|
||||
- [ ] Build and run the sample, you may have to fix some API break
|
||||
- [ ] Commit and push directly on `main`
|
||||
validations:
|
||||
|
|
|
@ -5,6 +5,12 @@ on:
|
|||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
CI_GRADLE_ARG_PROPERTIES: >
|
||||
-Porg.gradle.jvmargs=-Xmx2g
|
||||
-Porg.gradle.parallel=false
|
||||
|
||||
jobs:
|
||||
debug:
|
||||
name: Build debug APKs (${{ matrix.target }})
|
||||
|
@ -25,7 +31,7 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Assemble ${{ matrix.target }} debug apk
|
||||
run: ./gradlew assemble${{ matrix.target }}Debug --stacktrace
|
||||
run: ./gradlew assemble${{ matrix.target }}Debug $CI_GRADLE_ARG_PROPERTIES --stacktrace
|
||||
- name: Upload ${{ matrix.target }} debug APKs
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
@ -48,7 +54,7 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Assemble GPlay unsigned apk
|
||||
run: ./gradlew clean assembleGplayRelease --stacktrace
|
||||
run: ./gradlew clean assembleGplayRelease $CI_GRADLE_ARG_PROPERTIES --stacktrace
|
||||
- name: Upload Gplay unsigned APKs
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
|
|
@ -5,6 +5,12 @@ on:
|
|||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
CI_GRADLE_ARG_PROPERTIES: >
|
||||
-Porg.gradle.jvmargs=-Xmx2g
|
||||
-Porg.gradle.parallel=false
|
||||
|
||||
jobs:
|
||||
# Temporary add build of Android tests, which cannot be run on the CI right now, but they need to at least compile
|
||||
# So it will be mandatory for this action to be successful on every PRs
|
||||
|
@ -22,7 +28,7 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Compile Android tests
|
||||
run: ./gradlew clean assembleAndroidTest --stacktrace -PallWarningsAsErrors=false
|
||||
run: ./gradlew clean assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
|
||||
|
||||
integration-tests:
|
||||
name: Integration Tests (Synapse)
|
||||
|
@ -30,9 +36,14 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
api-level: [21, 28, 30]
|
||||
api-level: [28]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: 11
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
|
@ -64,5 +75,12 @@ jobs:
|
|||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
#arch: x86_64
|
||||
#disable-animations: true
|
||||
# script: ./gradlew -PallWarningsAsErrors=false vector:connectedAndroidTest matrix-sdk-android:connectedAndroidTest
|
||||
script: ./gradlew -PallWarningsAsErrors=false connectedCheck
|
||||
arch: x86
|
||||
profile: Nexus 5X
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
emulator-build: 7425822
|
||||
script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace
|
||||
|
|
|
@ -5,6 +5,12 @@ on:
|
|||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
CI_GRADLE_ARG_PROPERTIES: >
|
||||
-Porg.gradle.jvmargs=-Xmx2g
|
||||
-Porg.gradle.parallel=false
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Sanity Tests (Synapse)
|
||||
|
@ -46,5 +52,5 @@ jobs:
|
|||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
script: ./gradlew -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest
|
||||
script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest
|
||||
|
||||
|
|
|
@ -5,6 +5,12 @@ on:
|
|||
push:
|
||||
branches: [main, develop]
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
CI_GRADLE_ARG_PROPERTIES: >
|
||||
-Porg.gradle.jvmargs=-Xmx2g
|
||||
-Porg.gradle.parallel=false
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Run Unit Tests
|
||||
|
@ -20,4 +26,11 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Run unit tests
|
||||
run: ./gradlew clean test --stacktrace -PallWarningsAsErrors=false
|
||||
run: ./gradlew clean test $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false --stacktrace
|
||||
- name: Publish Unit Test Results
|
||||
uses: EnricoMi/publish-unit-test-result-action@v1
|
||||
if: always() &&
|
||||
github.event.sender.login != 'dependabot[bot]' &&
|
||||
( github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository )
|
||||
with:
|
||||
files: ./**/build/test-results/**/*.xml
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
.idea/*.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/benchmark-out
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
|
||||
|
|
50
CHANGES.md
50
CHANGES.md
|
@ -1,3 +1,53 @@
|
|||
Changes in Element v1.3.0 (2021-09-27)
|
||||
======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Spaces!
|
||||
- Adds email notification registration to Settings ([#2243](https://github.com/vector-im/element-android/issues/2243))
|
||||
- Spaces | M3.23 Invite by email in create private space flow ([#3678](https://github.com/vector-im/element-android/issues/3678))
|
||||
- Improve space invite bottom sheet ([#4057](https://github.com/vector-im/element-android/issues/4057))
|
||||
- Allow to also leave rooms when leaving a space ([#3692](https://github.com/vector-im/element-android/issues/3692))
|
||||
- Better expose adding spaces as Subspaces ([#3752](https://github.com/vector-im/element-android/issues/3752))
|
||||
- Push and syncs: add debug info on room list and on room detail screen and improves the log format. ([#4046](https://github.com/vector-im/element-android/issues/4046))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Remove the "Teammate spaces aren't quite ready" bottom sheet ([#3945](https://github.com/vector-im/element-android/issues/3945))
|
||||
- Restricted Room previews aren't working ([#3946](https://github.com/vector-im/element-android/issues/3946))
|
||||
- A removed room from a space can't be re-added as it won't be shown in add-room ([#3947](https://github.com/vector-im/element-android/issues/3947))
|
||||
- "Non-Admin" user able to invite others to Private Space (by default) ([#3951](https://github.com/vector-im/element-android/issues/3951))
|
||||
- Kick user dialog for spaces talks about rooms ([#3956](https://github.com/vector-im/element-android/issues/3956))
|
||||
- Messages are displayed as unable to decrypt then decrypted a few seconds later ([#4011](https://github.com/vector-im/element-android/issues/4011))
|
||||
- Fix DTMF not working ([#4015](https://github.com/vector-im/element-android/issues/4015))
|
||||
- Fix sticky end call notification ([#4019](https://github.com/vector-im/element-android/issues/4019))
|
||||
- Fix call screen stuck with some hanging up scenarios ([#4026](https://github.com/vector-im/element-android/issues/4026))
|
||||
- Fix other call not always refreshed when ended ([#4028](https://github.com/vector-im/element-android/issues/4028))
|
||||
- Private space invite bottomsheet only offering inviting by username not by email ([#4042](https://github.com/vector-im/element-android/issues/4042))
|
||||
- Spaces invitation system notifications don't take me to the join space toast ([#4043](https://github.com/vector-im/element-android/issues/4043))
|
||||
- Space Invites are not lighting up the drawer menu ([#4059](https://github.com/vector-im/element-android/issues/4059))
|
||||
- MessageActionsBottomSheet not being shown on local echos ([#4068](https://github.com/vector-im/element-android/issues/4068))
|
||||
|
||||
SDK API changes ⚠️
|
||||
------------------
|
||||
- InitialSyncProgressService has been renamed to SyncStatusService and its function getInitialSyncProgressStatus() has been renamed to getSyncStatusLive() ([#4046](https://github.com/vector-im/element-android/issues/4046))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- Better support for Sdk2 version. Also slight change in the default user agent: `MatrixAndroidSDK_X` is replaced by `MatrixAndroidSdk2` ([#3994](https://github.com/vector-im/element-android/issues/3994))
|
||||
- Introduces ConferenceEvent to abstract usage of Jitsi BroadcastEvent class. ([#4014](https://github.com/vector-im/element-android/issues/4014))
|
||||
- Improve performances on RoomDetail screen ([#4065](https://github.com/vector-im/element-android/issues/4065))
|
||||
|
||||
|
||||
Changes in Element v1.2.2 (2021-09-13)
|
||||
======================================
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
|
||||
- Fix a security issue with message key sharing. See https://matrix.org/blog/2021/09/13/vulnerability-disclosure-key-sharing for details.
|
||||
|
||||
|
||||
Changes in Element v1.2.1 (2021-09-08)
|
||||
======================================
|
||||
|
||||
|
|
|
@ -18,13 +18,12 @@ apply plugin: 'com.android.library'
|
|||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
compileSdk versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@ -34,8 +33,8 @@ android {
|
|||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility versions.sourceCompat
|
||||
targetCompatibility versions.targetCompat
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
|
@ -51,13 +50,13 @@ dependencies {
|
|||
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation libs.rx.rxKotlin
|
||||
implementation libs.rx.rxAndroid
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation libs.jetbrains.kotlinStdlib
|
||||
implementation libs.androidx.core
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.recyclerview
|
||||
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation libs.google.material
|
||||
}
|
11
build.gradle
11
build.gradle
|
@ -1,9 +1,9 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
ext.kotlin_version = '1.5.21'
|
||||
ext.kotlin_coroutines_version = "1.5.0"
|
||||
|
||||
apply from: 'dependencies.gradle'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
@ -11,12 +11,13 @@ buildscript {
|
|||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Release notes of Android Gradle Plugin (AGP):
|
||||
// https://developer.android.com/studio/releases/gradle-plugin
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
classpath libs.gradle.gradlePlugin
|
||||
classpath libs.gradle.kotlinPlugin
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
|
||||
classpath "com.likethesalad.android:string-reference:1.2.2"
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
ext.versions = [
|
||||
|
||||
'minSdk' : 21,
|
||||
'compileSdk' : 30,
|
||||
'targetSdk' : 30,
|
||||
'sourceCompat' : JavaVersion.VERSION_11,
|
||||
'targetCompat' : JavaVersion.VERSION_11,
|
||||
]
|
||||
|
||||
def gradle = "7.0.2"
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
def kotlin = "1.5.30"
|
||||
def kotlinCoroutines = "1.5.1"
|
||||
def dagger = "2.38.1"
|
||||
def retrofit = "2.9.0"
|
||||
def arrow = "0.8.2"
|
||||
def markwon = "4.6.2"
|
||||
def moshi = "1.12.0"
|
||||
def lifecycle = "2.2.0"
|
||||
def rxBinding = "3.1.0"
|
||||
def epoxy = "4.6.2"
|
||||
def glide = "4.12.0"
|
||||
def bigImageViewer = "1.8.1"
|
||||
def jjwt = "0.11.2"
|
||||
|
||||
// Testing
|
||||
def mockk = "1.12.0"
|
||||
def espresso = "3.4.0"
|
||||
def androidxTest = "1.4.0"
|
||||
|
||||
|
||||
ext.libs = [
|
||||
gradle : [
|
||||
'gradlePlugin' : "com.android.tools.build:gradle:$gradle",
|
||||
'kotlinPlugin' : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin"
|
||||
],
|
||||
jetbrains : [
|
||||
'kotlinStdlibJdk7' : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin",
|
||||
'kotlinStdlib' : "org.jetbrains.kotlin:kotlin-stdlib:$kotlin",
|
||||
'kotlinReflect' : "org.jetbrains.kotlin:kotlin-reflect:$kotlin",
|
||||
'coroutinesCore' : "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutines",
|
||||
'coroutinesAndroid' : "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutines",
|
||||
'coroutinesRx2' : "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutines"
|
||||
],
|
||||
androidx : [
|
||||
'appCompat' : "androidx.appcompat:appcompat:1.3.1",
|
||||
'core' : "androidx.core:core-ktx:1.6.0",
|
||||
'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1",
|
||||
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3",
|
||||
'fragmentKtx' : "androidx.fragment:fragment-ktx:1.3.6",
|
||||
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.0",
|
||||
'work' : "androidx.work:work-runtime-ktx:2.5.0",
|
||||
'autoFill' : "androidx.autofill:autofill:1.1.0",
|
||||
'preferenceKtx' : "androidx.preference:preference-ktx:1.1.1",
|
||||
'junit' : "androidx.test.ext:junit:1.1.3",
|
||||
'lifecycleExtensions' : "androidx.lifecycle:lifecycle-extensions:$lifecycle",
|
||||
'lifecycleJava8' : "androidx.lifecycle:lifecycle-common-java8:$lifecycle",
|
||||
'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1",
|
||||
'datastore' : "androidx.datastore:datastore:1.0.0",
|
||||
'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0",
|
||||
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
|
||||
'coreTesting' : "androidx.arch.core:core-testing:2.1.0",
|
||||
'testCore' : "androidx.test:core:$androidxTest",
|
||||
'orchestrator' : "androidx.test:orchestrator:$androidxTest",
|
||||
'testRunner' : "androidx.test:runner:$androidxTest",
|
||||
'testRules' : "androidx.test:rules:$androidxTest",
|
||||
'espressoCore' : "androidx.test.espresso:espresso-core:$espresso",
|
||||
'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso",
|
||||
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso"
|
||||
],
|
||||
google : [
|
||||
'material' : "com.google.android.material:material:1.4.0"
|
||||
],
|
||||
dagger : [
|
||||
'dagger' : "com.google.dagger:dagger:$dagger",
|
||||
'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger"
|
||||
],
|
||||
squareup : [
|
||||
'moshi' : "com.squareup.moshi:moshi-adapters:$moshi",
|
||||
'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi",
|
||||
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
|
||||
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
|
||||
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
|
||||
],
|
||||
rx : [
|
||||
'rxKotlin' : "io.reactivex.rxjava2:rxkotlin:2.4.0",
|
||||
'rxAndroid' : "io.reactivex.rxjava2:rxandroid:2.1.1"
|
||||
],
|
||||
arrow : [
|
||||
'core' : "io.arrow-kt:arrow-core:$arrow",
|
||||
'instances' : "io.arrow-kt:arrow-instances-core:$arrow"
|
||||
],
|
||||
markwon : [
|
||||
'core' : "io.noties.markwon:core:$markwon",
|
||||
'html' : "io.noties.markwon:html:$markwon"
|
||||
],
|
||||
airbnb : [
|
||||
'epoxy' : "com.airbnb.android:epoxy:$epoxy",
|
||||
'epoxyGlide' : "com.airbnb.android:epoxy-glide-preloading:$epoxy",
|
||||
'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy",
|
||||
'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy",
|
||||
'mvrx' : "com.airbnb.android:mvrx:1.5.1"
|
||||
],
|
||||
mockk : [
|
||||
'mockk' : "io.mockk:mockk:$mockk",
|
||||
'mockkAndroid' : "io.mockk:mockk-android:$mockk"
|
||||
],
|
||||
github : [
|
||||
'glide' : "com.github.bumptech.glide:glide:$glide",
|
||||
'glideCompiler' : "com.github.bumptech.glide:compiler:$glide",
|
||||
'bigImageViewer' : "com.github.piasy:BigImageViewer:$bigImageViewer",
|
||||
'glideImageLoader' : "com.github.piasy:GlideImageLoader:$bigImageViewer",
|
||||
'progressPieIndicator' : "com.github.piasy:ProgressPieIndicator:$bigImageViewer",
|
||||
'glideImageViewFactory' : "com.github.piasy:GlideImageViewFactory:$bigImageViewer"
|
||||
],
|
||||
jakewharton : [
|
||||
'timber' : "com.jakewharton.timber:timber:5.0.1",
|
||||
'rxbinding' : "com.jakewharton.rxbinding3:rxbinding:$rxBinding",
|
||||
'rxbindingAppcompat' : "com.jakewharton.rxbinding3:rxbinding-appcompat:$rxBinding",
|
||||
'rxbindingMaterial' : "com.jakewharton.rxbinding3:rxbinding-material:$rxBinding"
|
||||
],
|
||||
jsonwebtoken: [
|
||||
'jjwtApi' : "io.jsonwebtoken:jjwt-api:$jjwt",
|
||||
'jjwtImpl' : "io.jsonwebtoken:jjwt-impl:$jjwt",
|
||||
'jjwtOrgjson' : "io.jsonwebtoken:jjwt-orgjson:$jjwt"
|
||||
],
|
||||
tests : [
|
||||
'kluent' : "org.amshove.kluent:kluent-android:1.68",
|
||||
'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1",
|
||||
'junit' : "junit:junit:4.13.2"
|
||||
]
|
||||
]
|
|
@ -0,0 +1,2 @@
|
|||
Hauptänderungen: Sprachnachrichten standardmäßig aktiviert.
|
||||
Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Organize your rooms using Spaces!
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.0
|
|
@ -0,0 +1,2 @@
|
|||
Principaux changements pour cette version : messages vocaux activés par défault.
|
||||
Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Memperbaiki kesalahan saat mengirim pesan terenkripsi jika seseorang yang ada di ruangan keluar.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Pesan Suara diaktifkan secara default
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
|
@ -8,10 +8,10 @@ Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas ya
|
|||
- Obrolan video dengan VoIP dan berbagi layar
|
||||
- Integrasi yang mudah dengan alat kolaborasi online favorit Anda, alat manajemen proyek, layanan VoIP dan aplikasi perpesanan tim lainnya
|
||||
|
||||
Element benar-benar berbeda dari aplikasi perpesanan dan kolaborasi lainnya. Ini beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi. Ini memungkinkan hosting sendiri untuk memberi pengguna kepemilikan maksimum dan kontrol data dan pesan mereka.
|
||||
Element benar-benar berbeda dari aplikasi perpesanan dan kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi. Matrix memungkinkan hosting sendiri untuk memberi pengguna kepemilikan maksimum dan kontrol data dan pesan mereka.
|
||||
|
||||
<b>Pesan privasi dan terenkripsi</b>
|
||||
Element melindungi Anda dari iklan yang tidak diinginkan, data penambangan dan taman berdinding. Ini juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang di-cross-signed.
|
||||
Element melindungi Anda dari iklan yang tidak diinginkan, data penambangan dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang ditanda tangani silang.
|
||||
|
||||
Element memberi Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan aman dengan siapa pun di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan dengan aplikasi seperti Slack.
|
||||
|
||||
|
@ -30,7 +30,7 @@ Element menempatkan Anda dalam kendali dengan cara yang berbeda:
|
|||
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, apakah mereka menggunakan Element, aplikasi Matrix lain atau bahkan jika mereka menggunakan aplikasi perpesanan yang berbeda.
|
||||
|
||||
<b>Sangat aman</b>
|
||||
Enkripsi ujung-ke-ujung beneran (hanya mereka yang dalam percakapan dapat mendekripsi pesan), dan verifikasi perangkat yang di-cross-signed.
|
||||
Enkripsi ujung-ke-ujung beneran (hanya mereka yang dalam percakapan dapat mendekripsi pesan), dan verifikasi perangkat yang ditanda tangani silang.
|
||||
|
||||
<b>Komunikasi dan integrasi lengkap</b>
|
||||
Perpesanan, panggilan suara dan video, berbagi file, berbagi layar dan banyak integrasi, bot dan widget. Buat ruangan, komunitas, tetap terhubung dan selesaikan hal-hal.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: реализация голосовых сообщений в настройках лабораторий.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.15
|
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: Исправление ошибки при отправке зашифрованного сообщения, если кто-то в комнате выходит.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: Голосовое сообщение включено по умолчанию.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
|
@ -6,20 +6,20 @@
|
|||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
org.gradle.jvmargs=-Xmx4096m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# Enable file system watch (https://docs.gradle.org/6.7/release-notes.html)
|
||||
# Build Time Optimizations
|
||||
org.gradle.jvmargs=-Xmx3g -Xms512M -XX:MaxPermSize=2048m -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.vfs.watch=true
|
||||
|
||||
# Android Settings
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
||||
#Project Settings
|
||||
# Change debugPrivateData to true for debugging
|
||||
vector.debugPrivateData=false
|
||||
# httpLogLevel values: NONE, BASIC, HEADERS, BODY
|
||||
vector.httpLogLevel=BASIC
|
||||
|
||||
# Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above
|
||||
#vector.debugPrivateData=true
|
||||
#vector.httpLogLevel=BODY
|
||||
|
|
|
@ -20,14 +20,11 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
|
||||
compileSdk versions.compileSdk
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
@ -41,8 +38,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility versions.sourceCompat
|
||||
targetCompatibility versions.targetCompat
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
|
@ -55,10 +52,10 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.google.material
|
||||
// Pref theme
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation libs.androidx.preferenceKtx
|
||||
// PFLockScreen attrs
|
||||
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
|
||||
// dialpad dimen
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<attr name="actionDescription" format="string" />
|
||||
<attr name="leftIcon" format="reference" />
|
||||
<attr name="rightIcon" format="reference" />
|
||||
<attr name="betaAction" format="boolean" />
|
||||
<attr name="forceStartPadding" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
|
|
|
@ -3,13 +3,11 @@ apply plugin: 'kotlin-android'
|
|||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdk versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
|
||||
// Multidex is useful for tests
|
||||
multiDexEnabled true
|
||||
|
@ -24,8 +22,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility versions.sourceCompat
|
||||
targetCompatibility versions.targetCompat
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
|
@ -34,15 +32,16 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlin_coroutines_version"
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.rx.rxKotlin
|
||||
implementation libs.rx.rxAndroid
|
||||
implementation libs.jetbrains.coroutinesRx2
|
||||
|
||||
// Paging
|
||||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
implementation libs.androidx.pagingRuntimeKtx
|
||||
|
||||
// Logging
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation libs.jakewharton.timber
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S
|
|||
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.group.GroupSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.group.model.GroupSummary
|
||||
import org.matrix.android.sdk.api.session.identity.FoundThreePid
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
|
@ -239,6 +240,10 @@ class RxSession(private val session: Session) {
|
|||
)
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
fun lookupThreePid(threePid: ThreePid): Single<Optional<FoundThreePid>> = rxSingle {
|
||||
session.identityService().lookUp(listOf(threePid)).firstOrNull().toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun Session.rx(): RxSession {
|
||||
|
|
|
@ -14,14 +14,14 @@ buildscript {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
testOptions.unitTests.includeAndroidResources = true
|
||||
|
||||
compileSdk versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "0.0.1"
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
|
||||
// Multidex is useful for tests
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
@ -31,9 +31,7 @@ android {
|
|||
// that the app's state is completely cleared between tests.
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
|
||||
// Seems that the build tools 4.1.0 does not generate BuildConfig.VERSION_NAME anymore.
|
||||
// Add it manually here. We may remove this trick in the future
|
||||
buildConfigField "String", "VERSION_NAME", "\"0.0.1\""
|
||||
buildConfigField "String", "SDK_VERSION", "\"1.2.2\""
|
||||
|
||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||
resValue "string", "git_sdk_revision", "\"${gitRevision()}\""
|
||||
|
@ -68,8 +66,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility versions.sourceCompat
|
||||
targetCompatibility versions.targetCompat
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
|
@ -103,92 +101,83 @@ static def gitRevisionDate() {
|
|||
|
||||
dependencies {
|
||||
|
||||
def arrow_version = "0.8.2"
|
||||
def moshi_version = '1.12.0'
|
||||
def lifecycle_version = '2.2.0'
|
||||
def arch_version = '2.1.0'
|
||||
def markwon_version = '3.1.0'
|
||||
def daggerVersion = '2.38.1'
|
||||
def work_version = '2.5.0'
|
||||
def retrofit_version = '2.9.0'
|
||||
implementation libs.jetbrains.kotlinStdlibJdk7
|
||||
implementation libs.jetbrains.coroutinesCore
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.core
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.3.1"
|
||||
implementation "androidx.core:core-ktx:1.6.0"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
implementation libs.androidx.lifecycleExtensions
|
||||
implementation libs.androidx.lifecycleJava8
|
||||
|
||||
// Network
|
||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
|
||||
implementation libs.squareup.retrofit
|
||||
implementation libs.squareup.retrofitMoshi
|
||||
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))
|
||||
implementation 'com.squareup.okhttp3:okhttp'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor'
|
||||
implementation 'com.squareup.okhttp3:okhttp-urlconnection'
|
||||
|
||||
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||
implementation libs.squareup.moshi
|
||||
kapt libs.squareup.moshiKotlin
|
||||
|
||||
implementation "ru.noties.markwon:core:$markwon_version"
|
||||
implementation libs.markwon.core
|
||||
|
||||
// Image
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation libs.androidx.exifinterface
|
||||
|
||||
// Database
|
||||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||
kapt 'dk.ilios:realmfieldnameshelper:2.0.0'
|
||||
|
||||
// Work
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation libs.androidx.work
|
||||
|
||||
// FP
|
||||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
||||
implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
|
||||
implementation libs.arrow.core
|
||||
implementation libs.arrow.instances
|
||||
|
||||
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
|
||||
implementation 'org.matrix.gitlab.matrix-org:olm:3.2.4'
|
||||
|
||||
// DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
implementation libs.dagger.dagger
|
||||
kapt libs.dagger.daggerCompiler
|
||||
|
||||
// Logging
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation libs.jakewharton.timber
|
||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
|
||||
|
||||
// Video compression
|
||||
implementation 'com.otaliastudios:transcoder:0.10.3'
|
||||
implementation 'com.otaliastudios:transcoder:0.10.4'
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.33'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.5.1'
|
||||
testImplementation libs.tests.junit
|
||||
testImplementation 'org.robolectric:robolectric:4.6.1'
|
||||
//testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||
testImplementation 'io.mockk:mockk:1.12.0'
|
||||
testImplementation 'org.amshove.kluent:kluent-android:1.68'
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
testImplementation libs.mockk.mockk
|
||||
testImplementation libs.tests.kluent
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
// Plant Timber tree for test
|
||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
|
||||
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
androidTestImplementation 'androidx.test:core:1.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'org.amshove.kluent:kluent-android:1.68'
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.12.0'
|
||||
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
kaptAndroidTest libs.dagger.daggerCompiler
|
||||
androidTestImplementation libs.androidx.testCore
|
||||
androidTestImplementation libs.androidx.testRunner
|
||||
androidTestImplementation libs.androidx.testRules
|
||||
androidTestImplementation libs.androidx.junit
|
||||
androidTestImplementation libs.androidx.espressoCore
|
||||
androidTestImplementation libs.tests.kluent
|
||||
androidTestImplementation libs.mockk.mockkAndroid
|
||||
androidTestImplementation libs.androidx.coreTesting
|
||||
androidTestImplementation libs.jetbrains.coroutinesAndroid
|
||||
// Plant Timber tree for test
|
||||
androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
androidTestImplementation libs.tests.timberJunitRule
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.0'
|
||||
androidTestUtil libs.androidx.orchestrator
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
|||
}
|
||||
|
||||
fun getSdkVersion(): String {
|
||||
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
|
||||
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.matrix.android.sdk.common
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
|
||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
|
@ -59,13 +61,15 @@ class CommonTestHelper(context: Context) {
|
|||
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
||||
|
||||
init {
|
||||
Matrix.initialize(
|
||||
context,
|
||||
MatrixConfiguration(
|
||||
applicationFlavor = "TestFlavor",
|
||||
roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider()
|
||||
)
|
||||
)
|
||||
UiThreadStatement.runOnUiThread {
|
||||
Matrix.initialize(
|
||||
context,
|
||||
MatrixConfiguration(
|
||||
applicationFlavor = "TestFlavor",
|
||||
roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider()
|
||||
)
|
||||
)
|
||||
}
|
||||
matrix = Matrix.getInstance(context)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,19 @@ import org.junit.runners.JUnit4
|
|||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
|
@ -386,6 +395,8 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
// The room should have disapear from flat children
|
||||
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
|
||||
}
|
||||
|
||||
commonTestHelper.signOutAndClose(session)
|
||||
}
|
||||
|
||||
data class TestSpaceCreationResult(
|
||||
|
@ -434,6 +445,57 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
return TestSpaceCreationResult(spaceId, roomIds)
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
private fun createPrivateSpace(session: Session,
|
||||
spaceName: String,
|
||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||
/** Name, auto-join, canonical*/
|
||||
): TestSpaceCreationResult {
|
||||
var spaceId = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
|
||||
val roomIds =
|
||||
childInfo.map { entry ->
|
||||
var roomId = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
val homeServerCapabilities = session
|
||||
.getHomeServerCapabilities()
|
||||
roomId = session.createRoom(CreateRoomParams().apply {
|
||||
name = entry.first
|
||||
this.featurePreset = RestrictedRoomPreset(
|
||||
homeServerCapabilities,
|
||||
listOf(
|
||||
RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
|
||||
)
|
||||
)
|
||||
})
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
roomId
|
||||
}
|
||||
|
||||
roomIds.forEachIndexed { index, roomId ->
|
||||
runBlocking {
|
||||
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
|
||||
val canonical = childInfo[index].third
|
||||
if (canonical != null) {
|
||||
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
|
||||
}
|
||||
}
|
||||
}
|
||||
return TestSpaceCreationResult(spaceId, roomIds)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRootSpaces() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
@ -473,5 +535,111 @@ class SpaceHierarchyTest : InstrumentedTest {
|
|||
val rootSpaces = session.spaceService().getRootSpaceSummaries()
|
||||
|
||||
assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size)
|
||||
|
||||
commonTestHelper.signOutAndClose(session)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParentRelation() {
|
||||
val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true))
|
||||
val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true))
|
||||
|
||||
val spaceAInfo = createPrivateSpace(aliceSession, "Private Space A", listOf(
|
||||
Triple("General", true /*suggested*/, true/*canonical*/),
|
||||
Triple("Random", true, true)
|
||||
))
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
aliceSession.getRoom(spaceAInfo.spaceId)!!.invite(bobSession.myUserId, null)
|
||||
}
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
bobSession.joinRoom(spaceAInfo.spaceId, null, emptyList())
|
||||
}
|
||||
|
||||
var bobRoomId = ""
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
|
||||
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
aliceSession.joinRoom(bobRoomId)
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceSession.getRoomSummary(bobRoomId)?.membership?.isActive() == true
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val stateEvent = aliceSession.getRoom(bobRoomId)!!.getStateEvent(EventType.STATE_SPACE_PARENT, QueryStringValue.Equals(spaceAInfo.spaceId))
|
||||
stateEvent != null
|
||||
}
|
||||
}
|
||||
|
||||
// This should be an invalid space parent relation, because no opposite child and bob is not admin of the space
|
||||
commonTestHelper.runBlockingTest {
|
||||
// we can see the state event
|
||||
// but it is not valid and room is not in hierarchy
|
||||
assertTrue("Bob Room should not be listed as a child of the space", aliceSession.getRoomSummary(bobRoomId)?.flattenParentIds?.isEmpty() == true)
|
||||
}
|
||||
|
||||
// Let's now try to make alice admin of the room
|
||||
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
val room = bobSession.getRoom(bobRoomId)!!
|
||||
val currentPLContent = room
|
||||
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
||||
?.let { it.content.toModel<PowerLevelsContent>() }
|
||||
|
||||
val newPowerLevelsContent = currentPLContent
|
||||
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
|
||||
?.toContent()
|
||||
|
||||
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!!
|
||||
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
||||
?.content
|
||||
?.toModel<PowerLevelsContent>()
|
||||
?.let { PowerLevelsHelper(it) }
|
||||
powerLevelsHelper!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT)
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch {
|
||||
GlobalScope.launch {
|
||||
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
|
||||
it.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
bobSession.getRoomSummary(bobRoomId)?.flattenParentIds?.contains(spaceAInfo.spaceId) == true
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(bobSession)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
|||
}
|
||||
|
||||
fun getSdkVersion(): String {
|
||||
return BuildConfig.VERSION_NAME + " (" + BuildConfig.GIT_SDK_REVISION + ")"
|
||||
return BuildConfig.SDK_VERSION + " (" + BuildConfig.GIT_SDK_REVISION + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ package org.matrix.android.sdk.api.logger
|
|||
*/
|
||||
open class LoggerTag(_value: String, parentTag: LoggerTag? = null) {
|
||||
|
||||
object SYNC : LoggerTag("SYNC")
|
||||
object VOIP : LoggerTag("VOIP")
|
||||
|
||||
val value: String = if (parentTag == null) {
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.matrix.android.sdk.api.session.file.FileService
|
|||
import org.matrix.android.sdk.api.session.group.GroupService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
|
@ -75,7 +75,7 @@ interface Session :
|
|||
ProfileService,
|
||||
PushRuleService,
|
||||
PushersService,
|
||||
InitialSyncProgressService,
|
||||
SyncStatusService,
|
||||
HomeServerCapabilitiesService,
|
||||
SecureStorageService,
|
||||
AccountService {
|
||||
|
|
|
@ -239,7 +239,7 @@ data class Event(
|
|||
|
||||
fun Event.isTextMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_NOTICE -> true
|
||||
|
@ -249,7 +249,7 @@ fun Event.isTextMessage(): Boolean {
|
|||
|
||||
fun Event.isImageMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_IMAGE -> true
|
||||
else -> false
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ fun Event.isImageMessage(): Boolean {
|
|||
|
||||
fun Event.isVideoMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_VIDEO -> true
|
||||
else -> false
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ fun Event.isVideoMessage(): Boolean {
|
|||
|
||||
fun Event.isAudioMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_AUDIO -> true
|
||||
else -> false
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ fun Event.isAudioMessage(): Boolean {
|
|||
|
||||
fun Event.isFileMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ fun Event.isFileMessage(): Boolean {
|
|||
|
||||
fun Event.isAttachmentMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.initsync
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
|
||||
interface SyncStatusService {
|
||||
|
||||
fun getSyncStatusLive(): LiveData<Status>
|
||||
|
||||
sealed class Status {
|
||||
/**
|
||||
* For initial sync
|
||||
*/
|
||||
abstract class InitialSyncStatus: Status()
|
||||
|
||||
object Idle : InitialSyncStatus()
|
||||
data class Progressing(
|
||||
val initSyncStep: InitSyncStep,
|
||||
val percentProgress: Int = 0
|
||||
) : InitialSyncStatus()
|
||||
|
||||
/**
|
||||
* For incremental sync
|
||||
*/
|
||||
abstract class IncrementalSyncStatus: Status()
|
||||
|
||||
object IncrementalSyncIdle : IncrementalSyncStatus()
|
||||
data class IncrementalSyncParsing(
|
||||
val rooms: Int,
|
||||
val toDevice: Int
|
||||
) : IncrementalSyncStatus()
|
||||
object IncrementalSyncError : IncrementalSyncStatus()
|
||||
object IncrementalSyncDone : IncrementalSyncStatus()
|
||||
}
|
||||
}
|
|
@ -26,7 +26,14 @@ data class Pusher(
|
|||
val data: PusherData,
|
||||
|
||||
val state: PusherState
|
||||
)
|
||||
) {
|
||||
companion object {
|
||||
|
||||
const val KIND_EMAIL = "email"
|
||||
const val KIND_HTTP = "http"
|
||||
const val APP_ID_EMAIL = "m.email"
|
||||
}
|
||||
}
|
||||
|
||||
enum class PusherState {
|
||||
UNREGISTERED,
|
||||
|
|
|
@ -27,14 +27,12 @@ interface PushersService {
|
|||
|
||||
/**
|
||||
* Add a new HTTP pusher.
|
||||
* Note that only `http` kind is supported by the SDK for now.
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
|
||||
*
|
||||
* @param pushkey This is a unique identifier for this pusher. The value you should use for
|
||||
* this is the routing or destination address information for the notification,
|
||||
* for example, the APNS token for APNS or the Registration ID for GCM. If your
|
||||
* notification client has no such concept, use any unique identifier. Max length, 512 chars.
|
||||
* If the kind is "email", this is the email address to send notifications to.
|
||||
* @param appId the application id
|
||||
* This is a reverse-DNS style identifier for the application. It is recommended
|
||||
* that this end with the platform, such that different platform versions get
|
||||
|
@ -64,6 +62,30 @@ interface PushersService {
|
|||
append: Boolean,
|
||||
withEventIdOnly: Boolean): UUID
|
||||
|
||||
/**
|
||||
* Add a new Email pusher.
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
|
||||
*
|
||||
* @param email The email address to send notifications to.
|
||||
* @param lang The preferred language for receiving notifications (e.g. "en" or "en-US").
|
||||
* @param emailBranding The branding placeholder to include in the email communications.
|
||||
* @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher.
|
||||
* @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher.
|
||||
* @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition
|
||||
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
|
||||
* with the same App ID and pushkey for different users. Typically We always want to append for
|
||||
* email pushers since we don't want to stop other accounts notifying to the same email address.
|
||||
* @return A work request uuid. Can be used to listen to the status
|
||||
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
|
||||
* @throws [InvalidParameterException] if a parameter is not correct
|
||||
*/
|
||||
fun addEmailPusher(email: String,
|
||||
lang: String,
|
||||
emailBranding: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
append: Boolean = true): UUID
|
||||
|
||||
/**
|
||||
* Directly ask the push gateway to send a push to this device
|
||||
* If successful, the push gateway has accepted the request. In this case, the app should receive a Push with the provided eventId.
|
||||
|
@ -80,10 +102,23 @@ interface PushersService {
|
|||
eventId: String)
|
||||
|
||||
/**
|
||||
* Remove the http pusher
|
||||
* Remove a registered pusher
|
||||
* @param pusher the pusher to remove, can be http or email
|
||||
*/
|
||||
suspend fun removePusher(pusher: Pusher)
|
||||
|
||||
/**
|
||||
* Remove a Http pusher by its pushkey and appId
|
||||
* @see addHttpPusher
|
||||
*/
|
||||
suspend fun removeHttpPusher(pushkey: String, appId: String)
|
||||
|
||||
/**
|
||||
* Remove an Email pusher
|
||||
* @see addEmailPusher
|
||||
*/
|
||||
suspend fun removeEmailPusher(email: String)
|
||||
|
||||
/**
|
||||
* Get the current pushers, as a LiveData
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.matrix.android.sdk.api.session.room.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* These are the same fields as those returned by /publicRooms, with a few additions: room_type, membership and is_encrypted.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomStrippedState(
|
||||
/**
|
||||
* Aliases of the room. May be empty.
|
||||
*/
|
||||
@Json(name = "aliases")
|
||||
val aliases: List<String>? = null,
|
||||
|
||||
/**
|
||||
* The canonical alias of the room, if any.
|
||||
*/
|
||||
@Json(name = "canonical_alias")
|
||||
val canonicalAlias: String? = null,
|
||||
|
||||
/**
|
||||
* The name of the room, if any.
|
||||
*/
|
||||
@Json(name = "name")
|
||||
val name: String? = null,
|
||||
|
||||
/**
|
||||
* Required. The number of members joined to the room.
|
||||
*/
|
||||
@Json(name = "num_joined_members")
|
||||
val numJoinedMembers: Int = 0,
|
||||
|
||||
/**
|
||||
* Required. The ID of the room.
|
||||
*/
|
||||
@Json(name = "room_id")
|
||||
val roomId: String,
|
||||
|
||||
/**
|
||||
* The topic of the room, if any.
|
||||
*/
|
||||
@Json(name = "topic")
|
||||
val topic: String? = null,
|
||||
|
||||
/**
|
||||
* Required. Whether the room may be viewed by guest users without joining.
|
||||
*/
|
||||
@Json(name = "world_readable")
|
||||
val worldReadable: Boolean = false,
|
||||
|
||||
/**
|
||||
* Required. Whether guest users may join the room and participate in it. If they can,
|
||||
* they will be subject to ordinary power level rules like any other user.
|
||||
*/
|
||||
@Json(name = "guest_can_join")
|
||||
val guestCanJoin: Boolean = false,
|
||||
|
||||
/**
|
||||
* The URL for the room's avatar, if one is set.
|
||||
*/
|
||||
@Json(name = "avatar_url")
|
||||
val avatarUrl: String? = null,
|
||||
|
||||
/**
|
||||
* Undocumented item
|
||||
*/
|
||||
@Json(name = "m.federate")
|
||||
val isFederated: Boolean = false,
|
||||
|
||||
/**
|
||||
* Optional. If the room is encrypted. This is already accessible as stripped state.
|
||||
*/
|
||||
@Json(name = "is_encrypted")
|
||||
val isEncrypted: Boolean?,
|
||||
|
||||
/**
|
||||
* Optional. Type of the room, if any, i.e. m.space
|
||||
*/
|
||||
@Json(name = "room_type")
|
||||
val roomType: String?,
|
||||
|
||||
/**
|
||||
* The current membership of this user in the room. Usually leave if the room is fetched over federation.
|
||||
*/
|
||||
@Json(name = "membership")
|
||||
val membership: String?
|
||||
) {
|
||||
/**
|
||||
* Return the canonical alias, or the first alias from the list of aliases, or null
|
||||
*/
|
||||
fun getPrimaryAlias(): String? {
|
||||
return canonicalAlias ?: aliases?.firstOrNull()
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ data class MessageAudioContent(
|
|||
/**
|
||||
* Required. Must be 'm.audio'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. A description of the audio e.g. 'Bee Gees - Stayin' Alive', or some kind of content description for accessibility e.g. 'audio attachment'.
|
||||
|
|
|
@ -20,6 +20,11 @@ import org.matrix.android.sdk.api.session.events.model.Content
|
|||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
interface MessageContent {
|
||||
|
||||
companion object {
|
||||
const val MSG_TYPE_JSON_KEY = "msgtype"
|
||||
}
|
||||
|
||||
val msgType: String
|
||||
val body: String
|
||||
val relatesTo: RelationDefaultContent?
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageDefaultContent(
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
@Json(name = "body") override val body: String,
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
||||
|
|
|
@ -26,7 +26,7 @@ data class MessageEmoteContent(
|
|||
/**
|
||||
* Required. Must be 'm.emote'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. The emote action to perform.
|
||||
|
|
|
@ -28,7 +28,7 @@ data class MessageFileContent(
|
|||
/**
|
||||
* Required. Must be 'm.file'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. A human-readable description of the file. This is recommended to be the filename of the original upload.
|
||||
|
|
|
@ -27,7 +27,7 @@ data class MessageImageContent(
|
|||
/**
|
||||
* Required. Must be 'm.image'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. A textual representation of the image. This could be the alt text of the image, the filename of the image,
|
||||
|
|
|
@ -26,7 +26,7 @@ data class MessageLocationContent(
|
|||
/**
|
||||
* Required. Must be 'm.location'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. A description of the location e.g. 'Big Ben, London, UK', or some kind
|
||||
|
|
|
@ -26,7 +26,7 @@ data class MessageNoticeContent(
|
|||
/**
|
||||
* Required. Must be 'm.notice'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. The notice text to send.
|
||||
|
|
|
@ -30,7 +30,7 @@ const val OPTION_TYPE_BUTTONS = "org.matrix.buttons"
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageOptionsContent(
|
||||
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_OPTIONS,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_OPTIONS,
|
||||
@Json(name = "type") val optionType: String? = null,
|
||||
@Json(name = "body") override val body: String,
|
||||
@Json(name = "label") val label: String?,
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessagePollResponseContent(
|
||||
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_RESPONSE,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE,
|
||||
@Json(name = "body") override val body: String,
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null
|
||||
|
|
|
@ -26,7 +26,7 @@ data class MessageTextContent(
|
|||
/**
|
||||
* Required. Must be 'm.text'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. The body of the message.
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationInfoReque
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageVerificationRequestContent(
|
||||
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST,
|
||||
@Json(name = "body") override val body: String,
|
||||
@Json(name = "from_device") override val fromDevice: String?,
|
||||
@Json(name = "methods") override val methods: List<String>,
|
||||
|
|
|
@ -27,7 +27,7 @@ data class MessageVideoContent(
|
|||
/**
|
||||
* Required. Must be 'm.video'.
|
||||
*/
|
||||
@Json(name = "msgtype") override val msgType: String,
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String,
|
||||
|
||||
/**
|
||||
* Required. A description of the video e.g. 'Gangnam style', or some kind of content description for accessibility e.g. 'video attachment'.
|
||||
|
|
|
@ -94,5 +94,7 @@ interface SpaceService {
|
|||
*/
|
||||
suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List<String>)
|
||||
|
||||
suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String)
|
||||
|
||||
fun getRootSpaceSummaries(): List<RoomSummary>
|
||||
}
|
||||
|
|
|
@ -71,18 +71,24 @@ internal class InboundGroupSessionStore @Inject constructor(
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
fun storeInBoundGroupSession(wrapper: OlmInboundGroupSessionWrapper2) {
|
||||
fun storeInBoundGroupSession(wrapper: OlmInboundGroupSessionWrapper2, sessionId: String, senderKey: String) {
|
||||
Timber.v("## Inbound: getInboundGroupSession mark as dirty ${wrapper.roomId}-${wrapper.senderKey}")
|
||||
// We want to batch this a bit for performances
|
||||
dirtySession.add(wrapper)
|
||||
|
||||
if (sessionCache[CacheKey(sessionId, senderKey)] == null) {
|
||||
// first time seen, put it in memory cache while waiting for batch insert
|
||||
// If it's already known, no need to update cache it's already there
|
||||
sessionCache.put(CacheKey(sessionId, senderKey), wrapper)
|
||||
}
|
||||
|
||||
timerTask?.cancel()
|
||||
timerTask = object : TimerTask() {
|
||||
override fun run() {
|
||||
batchSave()
|
||||
}
|
||||
}
|
||||
timer.schedule(timerTask!!, 2_000)
|
||||
timer.schedule(timerTask!!, 300)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
|
|
@ -577,7 +577,8 @@ internal class MXOlmDevice @Inject constructor(
|
|||
session.keysClaimed = keysClaimed
|
||||
session.forwardingCurve25519KeyChain = forwardingCurve25519KeyChain
|
||||
|
||||
store.storeInboundGroupSessions(listOf(session))
|
||||
inboundGroupSessionStore.storeInBoundGroupSession(session, sessionId, senderKey)
|
||||
// store.storeInboundGroupSessions(listOf(session))
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -703,7 +704,7 @@ internal class MXOlmDevice @Inject constructor(
|
|||
timelineSet.add(messageIndexKey)
|
||||
}
|
||||
|
||||
inboundGroupSessionStore.storeInBoundGroupSession(session)
|
||||
inboundGroupSessionStore.storeInBoundGroupSession(session, sessionId, senderKey)
|
||||
val payload = try {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||
val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage)
|
||||
|
|
|
@ -36,7 +36,7 @@ internal class UserAgentHolder @Inject constructor(private val context: Context,
|
|||
|
||||
/**
|
||||
* Create an user agent with the application version.
|
||||
* Ex: Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSDK_X 1.0)
|
||||
* Ex: Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0)
|
||||
*
|
||||
* @param flavorDescription the flavor description
|
||||
*/
|
||||
|
@ -74,13 +74,13 @@ internal class UserAgentHolder @Inject constructor(private val context: Context,
|
|||
// if there is no user agent or cannot parse it
|
||||
if (null == systemUserAgent || systemUserAgent.lastIndexOf(")") == -1 || !systemUserAgent.contains("(")) {
|
||||
userAgent = (appName + "/" + appVersion + " ( Flavour " + flavorDescription
|
||||
+ "; MatrixAndroidSDK_X " + BuildConfig.VERSION_NAME + ")")
|
||||
+ "; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")")
|
||||
} else {
|
||||
// update
|
||||
userAgent = appName + "/" + appVersion + " " +
|
||||
systemUserAgent.substring(systemUserAgent.indexOf("("), systemUserAgent.lastIndexOf(")") - 1) +
|
||||
"; Flavour " + flavorDescription +
|
||||
"; MatrixAndroidSDK_X " + BuildConfig.VERSION_NAME + ")"
|
||||
"; MatrixAndroidSdk2 " + BuildConfig.SDK_VERSION + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
|
|||
import org.matrix.android.sdk.api.session.file.FileService
|
||||
import org.matrix.android.sdk.api.session.group.GroupService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
|
@ -115,7 +115,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||
private val typingUsersTracker: TypingUsersTracker,
|
||||
private val contentDownloadStateTracker: ContentDownloadStateTracker,
|
||||
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
|
||||
private val syncStatusService: Lazy<SyncStatusService>,
|
||||
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
||||
private val accountDataService: Lazy<SessionAccountDataService>,
|
||||
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||
|
@ -141,7 +141,7 @@ internal class DefaultSession @Inject constructor(
|
|||
PushersService by pushersService.get(),
|
||||
EventService by eventService.get(),
|
||||
TermsService by termsService.get(),
|
||||
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||
SyncStatusService by syncStatusService.get(),
|
||||
SecureStorageService by secureStorageService.get(),
|
||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||
ProfileService by profileService.get(),
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan
|
|||
import org.matrix.android.sdk.internal.session.media.MediaModule
|
||||
import org.matrix.android.sdk.internal.session.openid.OpenIdModule
|
||||
import org.matrix.android.sdk.internal.session.profile.ProfileModule
|
||||
import org.matrix.android.sdk.internal.session.pushers.AddHttpPusherWorker
|
||||
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
|
||||
import org.matrix.android.sdk.internal.session.pushers.PushersModule
|
||||
import org.matrix.android.sdk.internal.session.room.RoomModule
|
||||
import org.matrix.android.sdk.internal.session.room.relation.SendRelationWorker
|
||||
|
@ -127,7 +127,7 @@ internal interface SessionComponent {
|
|||
|
||||
fun inject(worker: SyncWorker)
|
||||
|
||||
fun inject(worker: AddHttpPusherWorker)
|
||||
fun inject(worker: AddPusherWorker)
|
||||
|
||||
fun inject(worker: SendVerificationMessageWorker)
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
|||
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
||||
import org.matrix.android.sdk.api.session.events.EventService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.securestorage.SecureStorageService
|
||||
|
@ -81,7 +81,7 @@ import org.matrix.android.sdk.internal.session.download.DownloadProgressIntercep
|
|||
import org.matrix.android.sdk.internal.session.events.DefaultEventService
|
||||
import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultSyncStatusService
|
||||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
||||
import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
|
||||
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
|
||||
|
@ -355,7 +355,7 @@ internal abstract class SessionModule {
|
|||
abstract fun bindEventSenderProcessorAsSessionLifecycleObserver(processor: EventSenderProcessorCoroutine): SessionLifecycleObserver
|
||||
|
||||
@Binds
|
||||
abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||
abstract fun bindSyncStatusService(service: DefaultSyncStatusService): SyncStatusService
|
||||
|
||||
@Binds
|
||||
abstract fun bindSecureStorageService(service: DefaultSecureStorageService): SecureStorageService
|
||||
|
|
|
@ -20,10 +20,16 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import org.matrix.android.sdk.api.auth.data.SessionParams
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
||||
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.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.identity.FoundThreePid
|
||||
|
@ -36,23 +42,17 @@ import org.matrix.android.sdk.internal.di.AuthenticatedIdentity
|
|||
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
|
||||
import org.matrix.android.sdk.internal.extensions.observeNotNull
|
||||
import org.matrix.android.sdk.internal.network.RetrofitFactory
|
||||
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.identity.data.IdentityStore
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask
|
||||
import org.matrix.android.sdk.internal.session.profile.BindThreePidsTask
|
||||
import org.matrix.android.sdk.internal.session.profile.UnbindThreePidsTask
|
||||
import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentityServerContent
|
||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
|
||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.internal.util.ensureProtocol
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
@ -202,6 +202,8 @@ internal class DefaultIdentityService @Inject constructor(
|
|||
|
||||
identityStore.setUrl(urlCandidate)
|
||||
identityStore.setToken(token)
|
||||
// could we remember if it was previously given?
|
||||
identityStore.setUserConsent(false)
|
||||
updateIdentityAPI(urlCandidate)
|
||||
|
||||
updateAccountData(urlCandidate)
|
||||
|
@ -230,6 +232,8 @@ internal class DefaultIdentityService @Inject constructor(
|
|||
}
|
||||
|
||||
override suspend fun lookUp(threePids: List<ThreePid>): List<FoundThreePid> {
|
||||
if (getCurrentIdentityServerUrl() == null) throw IdentityServiceError.NoIdentityServerConfigured
|
||||
|
||||
if (!getUserConsent()) {
|
||||
throw IdentityServiceError.UserConsentNotProvided
|
||||
}
|
||||
|
|
|
@ -18,23 +18,28 @@ package org.matrix.android.sdk.internal.session.initsync
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultInitialSyncProgressService @Inject constructor()
|
||||
: InitialSyncProgressService,
|
||||
internal class DefaultSyncStatusService @Inject constructor()
|
||||
: SyncStatusService,
|
||||
ProgressReporter {
|
||||
|
||||
private val status = MutableLiveData<InitialSyncProgressService.Status>()
|
||||
private val status = MutableLiveData<SyncStatusService.Status>()
|
||||
|
||||
private var rootTask: TaskInfo? = null
|
||||
|
||||
override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status> {
|
||||
override fun getSyncStatusLive(): LiveData<SyncStatusService.Status> {
|
||||
return status
|
||||
}
|
||||
|
||||
// Only to be used for incremental sync
|
||||
fun setStatus(newStatus: SyncStatusService.Status.IncrementalSyncStatus) {
|
||||
status.postValue(newStatus)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rootTask
|
||||
*/
|
||||
|
@ -67,7 +72,7 @@ internal class DefaultInitialSyncProgressService @Inject constructor()
|
|||
// Update the progress of the leaf and all its parents
|
||||
leaf.setProgress(progress)
|
||||
// Then update the live data using leaf wording and root progress
|
||||
status.postValue(InitialSyncProgressService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt()))
|
||||
status.postValue(SyncStatusService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,13 +87,13 @@ internal class DefaultInitialSyncProgressService @Inject constructor()
|
|||
// And close it
|
||||
endedTask.parent.child = null
|
||||
} else {
|
||||
status.postValue(InitialSyncProgressService.Status.Idle)
|
||||
status.postValue(SyncStatusService.Status.Idle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun endAll() {
|
||||
rootTask = null
|
||||
status.postValue(InitialSyncProgressService.Status.Idle)
|
||||
status.postValue(SyncStatusService.Status.Idle)
|
||||
}
|
||||
}
|
|
@ -33,8 +33,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
|||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
|
||||
: SessionSafeCoroutineWorker<AddHttpPusherWorker.Params>(context, params, Params::class.java) {
|
||||
internal class AddPusherWorker(context: Context, params: WorkerParameters)
|
||||
: SessionSafeCoroutineWorker<AddPusherWorker.Params>(context, params, Params::class.java) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
|
@ -66,27 +66,45 @@ internal class DefaultPushersService @Inject constructor(
|
|||
deviceDisplayName: String,
|
||||
url: String,
|
||||
append: Boolean,
|
||||
withEventIdOnly: Boolean)
|
||||
: UUID {
|
||||
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
|
||||
if (pushkey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
|
||||
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
|
||||
if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'")
|
||||
withEventIdOnly: Boolean
|
||||
) = addPusher(
|
||||
JsonPusher(
|
||||
pushKey = pushkey,
|
||||
kind = Pusher.KIND_HTTP,
|
||||
appId = appId,
|
||||
profileTag = profileTag,
|
||||
lang = lang,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
|
||||
append = append
|
||||
)
|
||||
)
|
||||
|
||||
val pusher = JsonPusher(
|
||||
pushKey = pushkey,
|
||||
kind = "http",
|
||||
appId = appId,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
profileTag = profileTag,
|
||||
lang = lang,
|
||||
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
|
||||
append = append)
|
||||
override fun addEmailPusher(email: String,
|
||||
lang: String,
|
||||
emailBranding: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
append: Boolean
|
||||
) = addPusher(
|
||||
JsonPusher(
|
||||
pushKey = email,
|
||||
kind = Pusher.KIND_EMAIL,
|
||||
appId = Pusher.APP_ID_EMAIL,
|
||||
profileTag = "",
|
||||
lang = lang,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
data = JsonPusherData(brand = emailBranding),
|
||||
append = append
|
||||
)
|
||||
)
|
||||
|
||||
val params = AddHttpPusherWorker.Params(sessionId, pusher)
|
||||
|
||||
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
|
||||
private fun addPusher(pusher: JsonPusher): UUID {
|
||||
pusher.validateParameters()
|
||||
val params = AddPusherWorker.Params(sessionId, pusher)
|
||||
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.setInputData(WorkerParamsFactory.toData(params))
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
|
||||
|
@ -95,8 +113,27 @@ internal class DefaultPushersService @Inject constructor(
|
|||
return request.id
|
||||
}
|
||||
|
||||
private fun JsonPusher.validateParameters() {
|
||||
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
|
||||
if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
|
||||
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
|
||||
data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") }
|
||||
}
|
||||
|
||||
override suspend fun removePusher(pusher: Pusher) {
|
||||
removePusher(pusher.pushKey, pusher.appId)
|
||||
}
|
||||
|
||||
override suspend fun removeHttpPusher(pushkey: String, appId: String) {
|
||||
val params = RemovePusherTask.Params(pushkey, appId)
|
||||
removePusher(pushkey, appId)
|
||||
}
|
||||
|
||||
override suspend fun removeEmailPusher(email: String) {
|
||||
removePusher(pushKey = email, Pusher.APP_ID_EMAIL)
|
||||
}
|
||||
|
||||
private suspend fun removePusher(pushKey: String, pushAppId: String) {
|
||||
val params = RemovePusherTask.Params(pushKey, pushAppId)
|
||||
removePusherTask.execute(params)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,5 +32,8 @@ internal data class JsonPusherData(
|
|||
* Currently the only format available is 'event_id_only'.
|
||||
*/
|
||||
@Json(name = "format")
|
||||
val format: String? = null
|
||||
val format: String? = null,
|
||||
|
||||
@Json(name = "brand")
|
||||
val brand: String? = null
|
||||
)
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.room
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomStrippedState
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetRoomSummaryTask : Task<GetRoomSummaryTask.Params, RoomStrippedState> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val viaServers: List<String>?
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultGetRoomSummaryTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : GetRoomSummaryTask {
|
||||
|
||||
override suspend fun execute(params: GetRoomSummaryTask.Params): RoomStrippedState {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
roomAPI.getRoomSummary(params.roomId, params.viaServers)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room
|
|||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomStrippedState
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
|
@ -254,7 +255,7 @@ internal interface RoomAPI {
|
|||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}")
|
||||
suspend fun join(@Path("roomIdOrAlias") roomIdOrAlias: String,
|
||||
@Query("server_name") viaServers: List<String>,
|
||||
@Body params: JsonDict): JoinRoomResponse
|
||||
@Body params: JsonDict): JoinRoomResponse
|
||||
|
||||
/**
|
||||
* Leave the given room.
|
||||
|
@ -381,4 +382,14 @@ internal interface RoomAPI {
|
|||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/upgrade")
|
||||
suspend fun upgradeRoom(@Path("roomId") roomId: String,
|
||||
@Body body: RoomUpgradeBody): RoomUpgradeResponse
|
||||
|
||||
/**
|
||||
* The API returns the summary of the specified room, if the room could be found and the client should be able to view
|
||||
* its contents according to the join_rules, history visibility, space membership and similar rules outlined in MSC3173
|
||||
* as well as if the user is already a member of that room.
|
||||
* https://github.com/deepbluev7/matrix-doc/blob/room-summaries/proposals/3266-room-summary.md
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "im.nheko.summary/rooms/{roomIdOrAlias}/summary")
|
||||
suspend fun getRoomSummary(@Path("roomIdOrAlias") roomidOrAlias: String,
|
||||
@Query("via") viaServers: List<String>?): RoomStrippedState
|
||||
}
|
||||
|
|
|
@ -258,4 +258,7 @@ internal abstract class RoomModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindSign3pidInvitationTask(task: DefaultSign3pidInvitationTask): Sign3pidInvitationTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetRoomSummaryTask(task: DefaultGetRoomSummaryTask): GetRoomSummaryTask
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFi
|
|||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.internal.session.room.GetRoomSummaryTask
|
||||
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
|
||||
|
@ -49,6 +50,7 @@ internal class DefaultPeekRoomTask @Inject constructor(
|
|||
private val getRoomIdByAliasTask: GetRoomIdByAliasTask,
|
||||
private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
|
||||
private val getPublicRoomTask: GetPublicRoomTask,
|
||||
private val getRoomSummaryTask: GetRoomSummaryTask,
|
||||
private val resolveRoomStateTask: ResolveRoomStateTask
|
||||
) : PeekRoomTask {
|
||||
|
||||
|
@ -70,6 +72,25 @@ internal class DefaultPeekRoomTask @Inject constructor(
|
|||
serverList = emptyList()
|
||||
}
|
||||
|
||||
// If the room summary API is available on the Home Server we should try it first
|
||||
val strippedState = tryOrNull("Failed to get room stripped state roomId:$roomId") {
|
||||
getRoomSummaryTask.execute(GetRoomSummaryTask.Params(roomId, serverList))
|
||||
}
|
||||
if (strippedState != null) {
|
||||
return PeekResult.Success(
|
||||
roomId = strippedState.roomId,
|
||||
alias = strippedState.getPrimaryAlias() ?: params.roomIdOrAlias.takeIf { isAlias },
|
||||
avatarUrl = strippedState.avatarUrl,
|
||||
name = strippedState.name,
|
||||
topic = strippedState.topic,
|
||||
numJoinedMembers = strippedState.numJoinedMembers,
|
||||
viaServers = serverList,
|
||||
roomType = strippedState.roomType,
|
||||
someMembers = null,
|
||||
isPublic = strippedState.worldReadable
|
||||
)
|
||||
}
|
||||
|
||||
// Is it a public room?
|
||||
val visibilityRes = tryOrNull("## PEEK: failed to get visibility") {
|
||||
getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
|||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
|
@ -31,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
|
@ -226,63 +228,102 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
}
|
||||
.toMap()
|
||||
|
||||
lookupMap.keys.forEach { lookedUp ->
|
||||
if (lookedUp.roomType == RoomType.SPACE) {
|
||||
// get childrens
|
||||
// First handle child relations
|
||||
lookupMap.keys.asSequence()
|
||||
.filter { it.roomType == RoomType.SPACE }
|
||||
.forEach { lookedUp ->
|
||||
// get childrens
|
||||
|
||||
lookedUp.children.clearWith { it.deleteFromRealm() }
|
||||
lookedUp.children.clearWith { it.deleteFromRealm() }
|
||||
|
||||
RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child ->
|
||||
RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child ->
|
||||
|
||||
lookedUp.children.add(
|
||||
realm.createObject<SpaceChildSummaryEntity>().apply {
|
||||
this.childRoomId = child.roomId
|
||||
this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst()
|
||||
this.order = child.order
|
||||
lookedUp.children.add(
|
||||
realm.createObject<SpaceChildSummaryEntity>().apply {
|
||||
this.childRoomId = child.roomId
|
||||
this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst()
|
||||
this.order = child.order
|
||||
// this.autoJoin = child.autoJoin
|
||||
this.viaServers.addAll(child.viaServers)
|
||||
}
|
||||
)
|
||||
this.viaServers.addAll(child.viaServers)
|
||||
}
|
||||
)
|
||||
|
||||
RoomSummaryEntity.where(realm, child.roomId)
|
||||
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
|
||||
.findFirst()
|
||||
?.let { childSum ->
|
||||
lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry ->
|
||||
if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) {
|
||||
// add looked up as a parent
|
||||
entry.value.add(childSum)
|
||||
RoomSummaryEntity.where(realm, child.roomId)
|
||||
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
|
||||
.findFirst()
|
||||
?.let { childSum ->
|
||||
lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry ->
|
||||
if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) {
|
||||
// add looked up as a parent
|
||||
entry.value.add(childSum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now let's check parent relations
|
||||
|
||||
lookupMap.keys
|
||||
.forEach { lookedUp ->
|
||||
lookedUp.parents.clearWith { it.deleteFromRealm() }
|
||||
// can we check parent relations here??
|
||||
/**
|
||||
* rooms can claim parents via the m.space.parent state event.
|
||||
* canonical determines whether this is the main parent for the space.
|
||||
*
|
||||
* To avoid abuse where a room admin falsely claims that a room is part of a space that it should not be,
|
||||
* clients could ignore such m.space.parent events unless either
|
||||
* (a) there is a corresponding m.space.child event in the claimed parent, or
|
||||
* (b) the sender of the m.space.child event has a sufficient power-level to send such an m.space.child event in the parent.
|
||||
* (It is not necessarily required that that user currently be a member of the parent room -
|
||||
* only the m.room.power_levels event is inspected.)
|
||||
* [Checking the power-level rather than requiring an actual m.space.child event in the parent allows for "secret" rooms (see below).]
|
||||
*/
|
||||
RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions()
|
||||
.map { parentInfo ->
|
||||
// Is it a valid parent relation?
|
||||
// Check if it's a child of the parent?
|
||||
val isValidRelation: Boolean
|
||||
val parent = lookupMap.firstNotNullOfOrNull { if (it.key.roomId == parentInfo.roomId) it.value else null }
|
||||
if (parent?.firstOrNull { it.roomId == lookedUp.roomId } != null) {
|
||||
// there is a corresponding m.space.child event in the claimed parent
|
||||
isValidRelation = true
|
||||
} else {
|
||||
// check if sender can post child relation in parent?
|
||||
val senderId = parentInfo.stateEventSender
|
||||
val parentRoomId = parentInfo.roomId
|
||||
val powerLevelsHelper = CurrentStateEventEntity
|
||||
.getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS)
|
||||
?.root
|
||||
?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() }
|
||||
?.let { PowerLevelsHelper(it) }
|
||||
|
||||
isValidRelation = powerLevelsHelper?.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) ?: false
|
||||
}
|
||||
|
||||
if (isValidRelation) {
|
||||
lookedUp.parents.add(
|
||||
realm.createObject<SpaceParentSummaryEntity>().apply {
|
||||
this.parentRoomId = parentInfo.roomId
|
||||
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
|
||||
this.canonical = parentInfo.canonical
|
||||
this.viaServers.addAll(parentInfo.viaServers)
|
||||
}
|
||||
)
|
||||
|
||||
RoomSummaryEntity.where(realm, parentInfo.roomId)
|
||||
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
|
||||
.findFirst()
|
||||
?.let { parentSum ->
|
||||
if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) {
|
||||
// add lookedup as a parent
|
||||
lookupMap[parentSum]?.add(lookedUp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lookedUp.parents.clearWith { it.deleteFromRealm() }
|
||||
// can we check parent relations here??
|
||||
RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions()
|
||||
.map { parentInfo ->
|
||||
|
||||
lookedUp.parents.add(
|
||||
realm.createObject<SpaceParentSummaryEntity>().apply {
|
||||
this.parentRoomId = parentInfo.roomId
|
||||
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
|
||||
this.canonical = parentInfo.canonical
|
||||
this.viaServers.addAll(parentInfo.viaServers)
|
||||
}
|
||||
)
|
||||
|
||||
RoomSummaryEntity.where(realm, parentInfo.roomId)
|
||||
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
|
||||
.findFirst()
|
||||
?.let { parentSum ->
|
||||
if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) {
|
||||
// add lookedup as a parent
|
||||
lookupMap[parentSum]?.add(lookedUp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple algorithm to break cycles
|
||||
// Need more work to decide how to break, probably need to be as consistent as possible
|
||||
|
|
|
@ -89,7 +89,6 @@ internal class DefaultSpace(
|
|||
body = SpaceChildContent(
|
||||
order = null,
|
||||
via = null,
|
||||
// autoJoin = null,
|
||||
suggested = null
|
||||
).toContent()
|
||||
)
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
|
||||
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
|
@ -77,7 +78,7 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
if (isPublic) {
|
||||
this.roomAliasName = roomAliasLocalPart
|
||||
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
|
||||
invite = 0
|
||||
invite = if (isPublic) Role.Default.value else Role.Moderator.value
|
||||
)
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
|
||||
|
@ -221,4 +222,23 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
).toContent()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String) {
|
||||
val room = roomGetter.getRoom(childRoomId)
|
||||
?: throw IllegalArgumentException("Unknown Room $childRoomId")
|
||||
|
||||
val existingEvent = room.getStateEvent(EventType.STATE_SPACE_PARENT, QueryStringValue.Equals(parentSpaceId))
|
||||
if (existingEvent != null) {
|
||||
// Should i check if it was sent by me?
|
||||
// we don't check power level, it will throw if you cannot do that
|
||||
room.sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_PARENT,
|
||||
stateKey = parentSpaceId,
|
||||
body = SpaceParentContent(
|
||||
via = null,
|
||||
canonical = null
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
package org.matrix.android.sdk.internal.session.sync
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
|
@ -26,7 +28,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
|
|||
import org.matrix.android.sdk.internal.network.toFailure
|
||||
import org.matrix.android.sdk.internal.session.filter.FilterRepository
|
||||
import org.matrix.android.sdk.internal.session.homeserver.GetHomeServerCapabilitiesTask
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultSyncStatusService
|
||||
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
||||
import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral
|
||||
import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseParser
|
||||
|
@ -40,6 +42,8 @@ import java.io.File
|
|||
import java.net.SocketTimeoutException
|
||||
import javax.inject.Inject
|
||||
|
||||
private val loggerTag = LoggerTag("SyncTask", LoggerTag.SYNC)
|
||||
|
||||
internal interface SyncTask : Task<SyncTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
|
@ -53,7 +57,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
@UserId private val userId: String,
|
||||
private val filterRepository: FilterRepository,
|
||||
private val syncResponseHandler: SyncResponseHandler,
|
||||
private val initialSyncProgressService: DefaultInitialSyncProgressService,
|
||||
private val defaultSyncStatusService: DefaultSyncStatusService,
|
||||
private val syncTokenStore: SyncTokenStore,
|
||||
private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask,
|
||||
private val userStore: UserStore,
|
||||
|
@ -75,7 +79,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun doSync(params: SyncTask.Params) {
|
||||
Timber.v("Sync task started on Thread: ${Thread.currentThread().name}")
|
||||
Timber.tag(loggerTag.value).d("Sync task started on Thread: ${Thread.currentThread().name}")
|
||||
|
||||
val requestParams = HashMap<String, String>()
|
||||
var timeout = 0L
|
||||
|
@ -92,7 +96,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
if (isInitialSync) {
|
||||
// We might want to get the user information in parallel too
|
||||
userStore.createOrUpdate(userId)
|
||||
initialSyncProgressService.startRoot(InitSyncStep.ImportingAccount, 100)
|
||||
defaultSyncStatusService.startRoot(InitSyncStep.ImportingAccount, 100)
|
||||
}
|
||||
// Maybe refresh the homeserver capabilities data we know
|
||||
getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false))
|
||||
|
@ -100,20 +104,20 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)
|
||||
|
||||
if (isInitialSync) {
|
||||
Timber.d("INIT_SYNC with filter: ${requestParams["filter"]}")
|
||||
Timber.tag(loggerTag.value).d("INIT_SYNC with filter: ${requestParams["filter"]}")
|
||||
val initSyncStrategy = initialSyncStrategy
|
||||
logDuration("INIT_SYNC strategy: $initSyncStrategy") {
|
||||
logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag) {
|
||||
if (initSyncStrategy is InitialSyncStrategy.Optimized) {
|
||||
roomSyncEphemeralTemporaryStore.reset()
|
||||
workingDir.mkdirs()
|
||||
val file = downloadInitSyncResponse(requestParams)
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.ImportingAccount, 1, 0.7F) {
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.ImportingAccount, 1, 0.7F) {
|
||||
handleSyncFile(file, initSyncStrategy)
|
||||
}
|
||||
// Delete all files
|
||||
workingDir.deleteRecursively()
|
||||
} else {
|
||||
val syncResponse = logDuration("INIT_SYNC Request") {
|
||||
val syncResponse = logDuration("INIT_SYNC Request", loggerTag) {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
syncAPI.sync(
|
||||
params = requestParams,
|
||||
|
@ -122,43 +126,60 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
logDuration("INIT_SYNC Database insertion") {
|
||||
syncResponseHandler.handleResponse(syncResponse, token, initialSyncProgressService)
|
||||
logDuration("INIT_SYNC Database insertion", loggerTag) {
|
||||
syncResponseHandler.handleResponse(syncResponse, token, defaultSyncStatusService)
|
||||
}
|
||||
}
|
||||
}
|
||||
initialSyncProgressService.endAll()
|
||||
defaultSyncStatusService.endAll()
|
||||
} else {
|
||||
val syncResponse = executeRequest(globalErrorReceiver) {
|
||||
syncAPI.sync(
|
||||
params = requestParams,
|
||||
readTimeOut = readTimeOut
|
||||
)
|
||||
Timber.tag(loggerTag.value).d("Start incremental sync request")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncIdle)
|
||||
val syncResponse = try {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
syncAPI.sync(
|
||||
params = requestParams,
|
||||
readTimeOut = readTimeOut
|
||||
)
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.tag(loggerTag.value).e(throwable, "Incremental sync request error")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncError)
|
||||
throw throwable
|
||||
}
|
||||
val nbRooms = syncResponse.rooms?.invite.orEmpty().size + syncResponse.rooms?.join.orEmpty().size + syncResponse.rooms?.leave.orEmpty().size
|
||||
val nbToDevice = syncResponse.toDevice?.events.orEmpty().size
|
||||
Timber.tag(loggerTag.value).d("Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s)")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncParsing(
|
||||
rooms = nbRooms,
|
||||
toDevice = nbToDevice
|
||||
))
|
||||
syncResponseHandler.handleResponse(syncResponse, token, null)
|
||||
Timber.tag(loggerTag.value).d("Incremental sync done")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncDone)
|
||||
}
|
||||
Timber.v("Sync task finished on Thread: ${Thread.currentThread().name}")
|
||||
Timber.tag(loggerTag.value).d("Sync task finished on Thread: ${Thread.currentThread().name}")
|
||||
}
|
||||
|
||||
private suspend fun downloadInitSyncResponse(requestParams: Map<String, String>): File {
|
||||
val workingFile = File(workingDir, "initSync.json")
|
||||
val status = initialSyncStatusRepository.getStep()
|
||||
if (workingFile.exists() && status >= InitialSyncStatus.STEP_DOWNLOADED) {
|
||||
Timber.d("INIT_SYNC file is already here")
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.3f) {
|
||||
Timber.tag(loggerTag.value).d("INIT_SYNC file is already here")
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.3f) {
|
||||
// Empty task
|
||||
}
|
||||
} else {
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADING)
|
||||
val syncResponse = logDuration("INIT_SYNC Perform server request") {
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.ServerComputing, 1, 0.2f) {
|
||||
val syncResponse = logDuration("INIT_SYNC Perform server request", loggerTag) {
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.ServerComputing, 1, 0.2f) {
|
||||
getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT)
|
||||
}
|
||||
}
|
||||
|
||||
if (syncResponse.isSuccessful) {
|
||||
logDuration("INIT_SYNC Download and save to file") {
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.1f) {
|
||||
logDuration("INIT_SYNC Download and save to file", loggerTag) {
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.1f) {
|
||||
syncResponse.body()?.byteStream()?.use { inputStream ->
|
||||
workingFile.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
|
@ -168,7 +189,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
} else {
|
||||
throw syncResponse.toFailure(globalErrorReceiver)
|
||||
.also { Timber.w("INIT_SYNC request failure: $this") }
|
||||
.also { Timber.tag(loggerTag.value).w("INIT_SYNC request failure: $this") }
|
||||
}
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADED)
|
||||
}
|
||||
|
@ -185,9 +206,9 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
).awaitResponse()
|
||||
} catch (throwable: Throwable) {
|
||||
if (throwable is SocketTimeoutException && retry > 0) {
|
||||
Timber.w("INIT_SYNC timeout retry left: $retry")
|
||||
Timber.tag(loggerTag.value).w("INIT_SYNC timeout retry left: $retry")
|
||||
} else {
|
||||
Timber.e(throwable, "INIT_SYNC timeout, no retry left, or other error")
|
||||
Timber.tag(loggerTag.value).e(throwable, "INIT_SYNC timeout, no retry left, or other error")
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
|
@ -195,18 +216,18 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized) {
|
||||
logDuration("INIT_SYNC handleSyncFile()") {
|
||||
val syncResponse = logDuration("INIT_SYNC Read file and parse") {
|
||||
logDuration("INIT_SYNC handleSyncFile()", loggerTag) {
|
||||
val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag) {
|
||||
syncResponseParser.parse(initSyncStrategy, workingFile)
|
||||
}
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_PARSED)
|
||||
// Log some stats
|
||||
val nbOfJoinedRooms = syncResponse.rooms?.join?.size ?: 0
|
||||
val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored }
|
||||
Timber.d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
|
||||
Timber.tag(loggerTag.value).d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
|
||||
|
||||
logDuration("INIT_SYNC Database insertion") {
|
||||
syncResponseHandler.handleResponse(syncResponse, null, initialSyncProgressService)
|
||||
logDuration("INIT_SYNC Database insertion", loggerTag) {
|
||||
syncResponseHandler.handleResponse(syncResponse, null, defaultSyncStatusService)
|
||||
}
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import kotlinx.coroutines.cancelChildren
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.call.MxCall
|
||||
import org.matrix.android.sdk.internal.session.call.ActiveCallHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncPresence
|
||||
|
@ -49,6 +50,8 @@ import kotlin.concurrent.schedule
|
|||
private const val RETRY_WAIT_TIME_MS = 10_000L
|
||||
private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
|
||||
|
||||
private val loggerTag = LoggerTag("SyncThread", LoggerTag.SYNC)
|
||||
|
||||
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||
private val networkConnectivityChecker: NetworkConnectivityChecker,
|
||||
private val backgroundDetectionObserver: BackgroundDetectionObserver,
|
||||
|
@ -83,7 +86,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
|
||||
fun restart() = synchronized(lock) {
|
||||
if (!isStarted) {
|
||||
Timber.v("Resume sync...")
|
||||
Timber.tag(loggerTag.value).d("Resume sync...")
|
||||
isStarted = true
|
||||
// Check again server availability and the token validity
|
||||
canReachServer = true
|
||||
|
@ -94,7 +97,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
|
||||
fun pause() = synchronized(lock) {
|
||||
if (isStarted) {
|
||||
Timber.v("Pause sync...")
|
||||
Timber.tag(loggerTag.value).d("Pause sync...")
|
||||
isStarted = false
|
||||
retryNoNetworkTask?.cancel()
|
||||
syncScope.coroutineContext.cancelChildren()
|
||||
|
@ -102,7 +105,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
|
||||
fun kill() = synchronized(lock) {
|
||||
Timber.v("Kill sync...")
|
||||
Timber.tag(loggerTag.value).d("Kill sync...")
|
||||
updateStateTo(SyncState.Killing)
|
||||
retryNoNetworkTask?.cancel()
|
||||
syncScope.coroutineContext.cancelChildren()
|
||||
|
@ -124,21 +127,21 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
|
||||
override fun run() {
|
||||
Timber.v("Start syncing...")
|
||||
Timber.tag(loggerTag.value).d("Start syncing...")
|
||||
|
||||
isStarted = true
|
||||
networkConnectivityChecker.register(this)
|
||||
backgroundDetectionObserver.register(this)
|
||||
registerActiveCallsObserver()
|
||||
while (state != SyncState.Killing) {
|
||||
Timber.v("Entering loop, state: $state")
|
||||
Timber.tag(loggerTag.value).d("Entering loop, state: $state")
|
||||
if (!isStarted) {
|
||||
Timber.v("Sync is Paused. Waiting...")
|
||||
Timber.tag(loggerTag.value).d("Sync is Paused. Waiting...")
|
||||
updateStateTo(SyncState.Paused)
|
||||
synchronized(lock) { lock.wait() }
|
||||
Timber.v("...unlocked")
|
||||
Timber.tag(loggerTag.value).d("...unlocked")
|
||||
} else if (!canReachServer) {
|
||||
Timber.v("No network. Waiting...")
|
||||
Timber.tag(loggerTag.value).d("No network. Waiting...")
|
||||
updateStateTo(SyncState.NoNetwork)
|
||||
// We force retrying in RETRY_WAIT_TIME_MS maximum. Otherwise it will be unlocked by onConnectivityChanged() or restart()
|
||||
retryNoNetworkTask = Timer(SyncState.NoNetwork.toString(), false).schedule(RETRY_WAIT_TIME_MS) {
|
||||
|
@ -148,19 +151,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
}
|
||||
synchronized(lock) { lock.wait() }
|
||||
Timber.v("...retry")
|
||||
Timber.tag(loggerTag.value).d("...retry")
|
||||
} else if (!isTokenValid) {
|
||||
Timber.v("Token is invalid. Waiting...")
|
||||
Timber.tag(loggerTag.value).d("Token is invalid. Waiting...")
|
||||
updateStateTo(SyncState.InvalidToken)
|
||||
synchronized(lock) { lock.wait() }
|
||||
Timber.v("...unlocked")
|
||||
Timber.tag(loggerTag.value).d("...unlocked")
|
||||
} else {
|
||||
if (state !is SyncState.Running) {
|
||||
updateStateTo(SyncState.Running(afterPause = true))
|
||||
}
|
||||
// No timeout after a pause
|
||||
val timeout = state.let { if (it is SyncState.Running && it.afterPause) 0 else DEFAULT_LONG_POOL_TIMEOUT }
|
||||
Timber.v("Execute sync request with timeout $timeout")
|
||||
Timber.tag(loggerTag.value).d("Execute sync request with timeout $timeout")
|
||||
val params = SyncTask.Params(timeout, SyncPresence.Online)
|
||||
val sync = syncScope.launch {
|
||||
doSync(params)
|
||||
|
@ -168,10 +171,10 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
runBlocking {
|
||||
sync.join()
|
||||
}
|
||||
Timber.v("...Continue")
|
||||
Timber.tag(loggerTag.value).d("...Continue")
|
||||
}
|
||||
}
|
||||
Timber.v("Sync killed")
|
||||
Timber.tag(loggerTag.value).d("Sync killed")
|
||||
updateStateTo(SyncState.Killed)
|
||||
backgroundDetectionObserver.unregister(this)
|
||||
networkConnectivityChecker.unregister(this)
|
||||
|
@ -199,19 +202,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
if (failure is Failure.NetworkConnection && failure.cause is SocketTimeoutException) {
|
||||
// Timeout are not critical
|
||||
Timber.v("Timeout")
|
||||
Timber.tag(loggerTag.value).d("Timeout")
|
||||
} else if (failure is CancellationException) {
|
||||
Timber.v("Cancelled")
|
||||
Timber.tag(loggerTag.value).d("Cancelled")
|
||||
} else if (failure.isTokenError()) {
|
||||
// No token or invalid token, stop the thread
|
||||
Timber.w(failure, "Token error")
|
||||
Timber.tag(loggerTag.value).w(failure, "Token error")
|
||||
isStarted = false
|
||||
isTokenValid = false
|
||||
} else {
|
||||
Timber.e(failure)
|
||||
Timber.tag(loggerTag.value).e(failure)
|
||||
if (failure !is Failure.NetworkConnection || failure.cause is JsonEncodingException) {
|
||||
// Wait 10s before retrying
|
||||
Timber.v("Wait 10s")
|
||||
Timber.tag(loggerTag.value).d("Wait 10s")
|
||||
delay(RETRY_WAIT_TIME_MS)
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +228,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
|
||||
private fun updateStateTo(newState: SyncState) {
|
||||
Timber.v("Update state from $state to $newState")
|
||||
Timber.tag(loggerTag.value).d("Update state from $state to $newState")
|
||||
if (newState == state) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.util
|
||||
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import timber.log.Timber
|
||||
|
||||
internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
||||
|
@ -32,14 +33,15 @@ internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
|||
}
|
||||
|
||||
internal suspend fun <T> logDuration(message: String,
|
||||
loggerTag: LoggerTag,
|
||||
block: suspend () -> T): T {
|
||||
Timber.d("$message -- BEGIN")
|
||||
Timber.tag(loggerTag.value).d("$message -- BEGIN")
|
||||
val start = System.currentTimeMillis()
|
||||
val result = logRamUsage(message) {
|
||||
block()
|
||||
}
|
||||
val duration = System.currentTimeMillis() - start
|
||||
Timber.d("$message -- END duration: $duration ms")
|
||||
Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms")
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -19,14 +19,11 @@ apply plugin: 'kotlin-android'
|
|||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdk versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles 'consumer-rules.pro'
|
||||
}
|
||||
|
@ -41,11 +38,11 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.6"
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation libs.jetbrains.kotlinStdlibJdk7
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.fragmentKtx
|
||||
implementation libs.androidx.exifinterface
|
||||
|
||||
// Log
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation libs.jakewharton.timber
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# Copyright 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.
|
||||
#
|
||||
|
||||
clean_assemble {
|
||||
tasks = ["clean", ":vector:assembleGPlayDebug"]
|
||||
}
|
||||
|
||||
clean_assemble_build_cache {
|
||||
tasks = ["clean", ":vector:assembleGPlayDebug"]
|
||||
gradle-args = ["--build-cache"]
|
||||
}
|
||||
|
||||
clean_assemble_without_cache {
|
||||
tasks = ["clean", ":vector:assembleGPlayDebug"]
|
||||
gradle-args = ["--no-build-cache"]
|
||||
}
|
||||
|
||||
incremental_assemble_sdk_abi {
|
||||
tasks = [":vector:assembleGPlayDebug"]
|
||||
apply-abi-change-to = "matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt"
|
||||
}
|
||||
|
||||
incremental_assemble_sdk_no_abi {
|
||||
tasks = [":vector:assembleGPlayDebug"]
|
||||
apply-non-abi-change-to = "matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt"
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Copyright 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.
|
||||
#
|
||||
|
||||
if ! command -v gradle-profiler &> /dev/null
|
||||
then
|
||||
echo "gradle-profiler could not be found https://github.com/gradle/gradle-profiler"
|
||||
exit
|
||||
fi
|
||||
|
||||
gradle-profiler \
|
||||
--benchmark \
|
||||
--project-dir . \
|
||||
--scenario-file tools/benchmark/benchmark.profile \
|
||||
--output-dir benchmark-out/output \
|
||||
--gradle-user-home benchmark-out/gradle-home \
|
||||
--warmups 3 \
|
||||
--iterations 3 \
|
||||
$1
|
|
@ -162,7 +162,7 @@ Formatter\.formatShortFileSize===1
|
|||
# android\.text\.TextUtils
|
||||
|
||||
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
|
||||
enum class===105
|
||||
enum class===106
|
||||
|
||||
### Do not import temporary legacy classes
|
||||
import org.matrix.android.sdk.internal.legacy.riot===3
|
||||
|
|
|
@ -13,8 +13,8 @@ kapt {
|
|||
|
||||
// Note: 2 digits max for each value
|
||||
ext.versionMajor = 1
|
||||
ext.versionMinor = 2
|
||||
ext.versionPatch = 2
|
||||
ext.versionMinor = 3
|
||||
ext.versionPatch = 0
|
||||
|
||||
ext.scVersion = 44
|
||||
|
||||
|
@ -104,17 +104,20 @@ ext.abiVersionCodes = ["armeabi-v7a": 1, "arm64-v8a": 2, "x86": 3, "x86_64": 4].
|
|||
def buildNumber = System.env.BUILDKITE_BUILD_NUMBER as Integer ?: 0
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
|
||||
|
||||
// Due to a bug introduced in Android gradle plugin 3.6.0, we have to specify the ndk version to use
|
||||
// Ref: https://issuetracker.google.com/issues/144111441
|
||||
ndkVersion "21.3.6528147"
|
||||
|
||||
compileSdk versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.spiritcroc.riotx"
|
||||
// Set to API 21: see #405
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
minSdk versions.minSdk
|
||||
targetSdk versions.targetSdk
|
||||
multiDexEnabled true
|
||||
|
||||
renderscriptTargetApi 24
|
||||
|
@ -294,8 +297,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility versions.sourceCompat
|
||||
targetCompatibility versions.targetCompat
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
|
@ -322,26 +325,6 @@ android {
|
|||
|
||||
dependencies {
|
||||
|
||||
def epoxy_version = '4.6.2'
|
||||
def fragment_version = '1.3.6'
|
||||
def arrow_version = "0.8.2"
|
||||
def markwon_version = '4.1.2'
|
||||
def big_image_viewer_version = '1.8.1'
|
||||
def glide_version = '4.12.0'
|
||||
def moshi_version = '1.12.0'
|
||||
def daggerVersion = '2.38.1'
|
||||
def autofill_version = "1.1.0"
|
||||
def work_version = '2.5.0'
|
||||
def arch_version = '2.1.0'
|
||||
def lifecycle_version = '2.2.0'
|
||||
def rxbinding_version = '3.1.0'
|
||||
def jjwt_version = '0.11.2'
|
||||
|
||||
// Tests
|
||||
def kluent_version = '1.68'
|
||||
def androidxTest_version = '1.4.0'
|
||||
def espresso_version = '3.4.0'
|
||||
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation project(":matrix-sdk-android-rx")
|
||||
implementation project(":diff-match-patch")
|
||||
|
@ -350,75 +333,79 @@ dependencies {
|
|||
implementation project(":library:ui-styles")
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
implementation libs.jetbrains.kotlinStdlibJdk7
|
||||
implementation libs.jetbrains.kotlinReflect
|
||||
implementation libs.jetbrains.coroutinesCore
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||
implementation libs.androidx.recyclerview
|
||||
implementation libs.androidx.appCompat
|
||||
implementation libs.androidx.fragmentKtx
|
||||
implementation libs.androidx.constraintLayout
|
||||
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation "androidx.media:media:1.4.1"
|
||||
implementation libs.androidx.core
|
||||
implementation "androidx.media:media:1.4.2"
|
||||
implementation "androidx.transition:transition:1.4.1"
|
||||
|
||||
implementation "org.threeten:threetenbp:1.4.0:no-tzdb"
|
||||
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.9.0"
|
||||
|
||||
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||
implementation libs.squareup.moshi
|
||||
implementation libs.squareup.moshiKt
|
||||
kapt libs.squareup.moshiKotlin
|
||||
implementation libs.androidx.lifecycleExtensions
|
||||
implementation libs.androidx.lifecycleLivedata
|
||||
|
||||
implementation libs.androidx.datastore
|
||||
implementation libs.androidx.datastorepreferences
|
||||
|
||||
|
||||
// Log
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation libs.jakewharton.timber
|
||||
|
||||
// Debug
|
||||
implementation 'com.facebook.stetho:stetho:1.6.0'
|
||||
|
||||
// Phone number https://github.com/google/libphonenumber
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31'
|
||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.33'
|
||||
|
||||
// rx
|
||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation libs.rx.rxKotlin
|
||||
implementation libs.rx.rxAndroid
|
||||
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.1'
|
||||
// RXBinding
|
||||
implementation "com.jakewharton.rxbinding3:rxbinding:$rxbinding_version"
|
||||
implementation "com.jakewharton.rxbinding3:rxbinding-appcompat:$rxbinding_version"
|
||||
implementation "com.jakewharton.rxbinding3:rxbinding-material:$rxbinding_version"
|
||||
implementation libs.jakewharton.rxbinding
|
||||
implementation libs.jakewharton.rxbindingAppcompat
|
||||
implementation libs.jakewharton.rxbindingMaterial
|
||||
|
||||
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
||||
implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version"
|
||||
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||
implementation "com.airbnb.android:epoxy-paging:$epoxy_version"
|
||||
implementation 'com.airbnb.android:mvrx:1.5.1'
|
||||
implementation libs.airbnb.epoxy
|
||||
implementation libs.airbnb.epoxyGlide
|
||||
kapt libs.airbnb.epoxyProcessor
|
||||
implementation libs.airbnb.epoxyPaging
|
||||
implementation libs.airbnb.mvrx
|
||||
|
||||
// Work
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation libs.androidx.work
|
||||
|
||||
// Paging
|
||||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
implementation libs.androidx.pagingRuntimeKtx
|
||||
|
||||
// Functional Programming
|
||||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
||||
implementation libs.arrow.core
|
||||
|
||||
// Pref
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation libs.androidx.preferenceKtx
|
||||
|
||||
// UI
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation libs.google.material
|
||||
implementation 'me.gujun.android:span:1.7'
|
||||
implementation "io.noties.markwon:core:$markwon_version"
|
||||
implementation "io.noties.markwon:html:$markwon_version"
|
||||
implementation libs.markwon.core
|
||||
implementation libs.markwon.html
|
||||
implementation 'com.googlecode.htmlcompressor:htmlcompressor:1.5.2'
|
||||
implementation 'me.saket:better-link-movement-method:2.2.0'
|
||||
implementation 'com.google.android:flexbox:2.0.1'
|
||||
implementation "androidx.autofill:autofill:$autofill_version"
|
||||
implementation libs.androidx.autoFill
|
||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
|
||||
implementation 'com.github.hyuwah:DraggableView:1.0.0'
|
||||
|
@ -433,7 +420,7 @@ dependencies {
|
|||
// To convert voice message on old platforms
|
||||
implementation 'com.arthenica:ffmpeg-kit-audio:4.4.LTS'
|
||||
|
||||
//Alerter
|
||||
// Alerter
|
||||
implementation 'com.tapadoo.android:alerter:7.0.1'
|
||||
|
||||
implementation 'com.otaliastudios:autocomplete:1.1.0'
|
||||
|
@ -442,16 +429,16 @@ dependencies {
|
|||
implementation 'com.squareup:seismic:1.0.2'
|
||||
|
||||
// Image Loading
|
||||
implementation "com.github.piasy:BigImageViewer:$big_image_viewer_version"
|
||||
implementation "com.github.piasy:GlideImageLoader:$big_image_viewer_version"
|
||||
implementation "com.github.piasy:ProgressPieIndicator:$big_image_viewer_version"
|
||||
implementation "com.github.piasy:GlideImageViewFactory:$big_image_viewer_version"
|
||||
implementation libs.github.bigImageViewer
|
||||
implementation libs.github.glideImageLoader
|
||||
implementation libs.github.progressPieIndicator
|
||||
implementation libs.github.glideImageViewFactory
|
||||
|
||||
// implementation 'com.github.MikeOrtiz:TouchImageView:3.0.2'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||
implementation libs.github.glide
|
||||
kapt libs.github.glideCompiler
|
||||
implementation 'com.danikula:videocache:2.7.1'
|
||||
implementation 'com.github.yalantis:ucrop:2.2.7'
|
||||
|
||||
|
@ -462,8 +449,8 @@ dependencies {
|
|||
implementation 'nl.dionsegijn:konfetti:1.3.2'
|
||||
implementation 'com.github.jetradarmobile:android-snowfall:1.2.1'
|
||||
// DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
implementation libs.dagger.dagger
|
||||
kapt libs.dagger.daggerCompiler
|
||||
|
||||
// UnifiedPush
|
||||
implementation 'com.github.UnifiedPush:android-connector:1.2.0'
|
||||
|
@ -505,36 +492,37 @@ dependencies {
|
|||
implementation 'im.dlg:android-dialer:1.2.5'
|
||||
|
||||
// JWT
|
||||
api "io.jsonwebtoken:jjwt-api:$jjwt_version"
|
||||
runtimeOnly "io.jsonwebtoken:jjwt-impl:$jjwt_version"
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-orgjson:$jjwt_version") {
|
||||
api libs.jsonwebtoken.jjwtApi
|
||||
runtimeOnly libs.jsonwebtoken.jjwtImpl
|
||||
runtimeOnly(libs.jsonwebtoken.jjwtOrgjson) {
|
||||
exclude group: 'org.json', module: 'json' //provided by Android natively
|
||||
}
|
||||
implementation 'commons-codec:commons-codec:1.15'
|
||||
|
||||
|
||||
// TESTS
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation "org.amshove.kluent:kluent-android:$kluent_version"
|
||||
testImplementation libs.tests.junit
|
||||
testImplementation libs.tests.kluent
|
||||
// Plant Timber tree for test
|
||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
testImplementation libs.tests.timberJunitRule
|
||||
|
||||
// Activate when you want to check for leaks, from time to time.
|
||||
//debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
|
||||
|
||||
androidTestImplementation "androidx.test:core:$androidxTest_version"
|
||||
androidTestImplementation "androidx.test:runner:$androidxTest_version"
|
||||
androidTestImplementation "androidx.test:rules:$androidxTest_version"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espresso_version"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-intents:$espresso_version"
|
||||
androidTestImplementation "org.amshove.kluent:kluent-android:$kluent_version"
|
||||
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"
|
||||
androidTestImplementation libs.androidx.testCore
|
||||
androidTestImplementation libs.androidx.testRunner
|
||||
androidTestImplementation libs.androidx.testRules
|
||||
androidTestImplementation libs.androidx.junit
|
||||
androidTestImplementation libs.androidx.espressoCore
|
||||
androidTestImplementation libs.androidx.espressoContrib
|
||||
androidTestImplementation libs.androidx.espressoIntents
|
||||
androidTestImplementation libs.tests.kluent
|
||||
androidTestImplementation libs.androidx.coreTesting
|
||||
// Plant Timber tree for test
|
||||
androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
androidTestImplementation libs.tests.timberJunitRule
|
||||
// "The one who serves a great Espresso"
|
||||
androidTestImplementation('com.adevinta.android:barista:4.1.0') {
|
||||
androidTestImplementation('com.adevinta.android:barista:4.2.0') {
|
||||
exclude group: 'org.jetbrains.kotlin'
|
||||
}
|
||||
androidTestUtil libs.androidx.orchestrator
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
<issue id="Recycle" severity="error" />
|
||||
<issue id="KotlinPropertyAccess" severity="error" />
|
||||
<issue id="DefaultLocale" severity="error" />
|
||||
<issue id="CheckResult" severity="error" />
|
||||
|
||||
<issue id="InvalidPackage">
|
||||
<!-- Ignore error from HtmlCompressor lib -->
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
package im.vector.app.features.reactions.data
|
||||
|
||||
import im.vector.app.InstrumentedTest
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
|
@ -30,64 +34,80 @@ import kotlin.system.measureTimeMillis
|
|||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class EmojiDataSourceTest : InstrumentedTest {
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
|
||||
@Test
|
||||
fun checkParsingTime() {
|
||||
val time = measureTimeMillis {
|
||||
EmojiDataSource(context().resources)
|
||||
createEmojiDataSource()
|
||||
}
|
||||
|
||||
assertTrue("Too long to parse", time < 100)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkNumberOfResult() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
assertTrue("Wrong number of emojis", emojiDataSource.rawData.emojis.size >= 500)
|
||||
assertTrue("Wrong number of categories", emojiDataSource.rawData.categories.size >= 8)
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val rawData = runBlocking {
|
||||
emojiDataSource.rawData.await()
|
||||
}
|
||||
assertTrue("Wrong number of emojis", rawData.emojis.size >= 500)
|
||||
assertTrue("Wrong number of categories", rawData.categories.size >= 8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchTestEmptySearch() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
|
||||
assertTrue("Empty search should return at least 500 results", emojiDataSource.filterWith("").size >= 500)
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val result = runBlocking {
|
||||
emojiDataSource.filterWith("")
|
||||
}
|
||||
assertTrue("Empty search should return at least 500 results", result.size >= 500)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchTestNoResult() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
|
||||
assertTrue("Should not have result", emojiDataSource.filterWith("noresult").isEmpty())
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val result = runBlocking {
|
||||
emojiDataSource.filterWith("noresult")
|
||||
}
|
||||
assertTrue("Should not have result", result.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchTestOneResult() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
|
||||
assertEquals("Should have 1 result", 1, emojiDataSource.filterWith("france").size)
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val result = runBlocking {
|
||||
emojiDataSource.filterWith("france")
|
||||
}
|
||||
assertEquals("Should have 1 result", 1, result.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchTestManyResult() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
|
||||
assertTrue("Should have many result", emojiDataSource.filterWith("fra").size > 1)
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val result = runBlocking {
|
||||
emojiDataSource.filterWith("fra")
|
||||
}
|
||||
assertTrue("Should have many result", result.size > 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTada() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
|
||||
val result = emojiDataSource.filterWith("tada")
|
||||
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val result = runBlocking {
|
||||
emojiDataSource.filterWith("tada")
|
||||
}
|
||||
assertEquals("Should find tada emoji", 1, result.size)
|
||||
assertEquals("Should find tada emoji", "🎉", result[0].emoji)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testQuickReactions() {
|
||||
val emojiDataSource = EmojiDataSource(context().resources)
|
||||
|
||||
assertEquals("Should have 8 quick reactions", 8, emojiDataSource.getQuickReactions().size)
|
||||
val emojiDataSource = createEmojiDataSource()
|
||||
val result = runBlocking {
|
||||
emojiDataSource.getQuickReactions()
|
||||
}
|
||||
assertEquals("Should have 8 quick reactions", 8, result.size)
|
||||
}
|
||||
|
||||
private fun createEmojiDataSource() = EmojiDataSource(coroutineScope, context().resources)
|
||||
}
|
||||
|
|
|
@ -225,6 +225,8 @@ class UiAllScreensSanityTest {
|
|||
clickOn(R.string.message_add_reaction)
|
||||
// Filter
|
||||
// TODO clickMenu(R.id.search)
|
||||
// Wait for emoji to load, it's async now
|
||||
sleep(1_000)
|
||||
clickListItem(R.id.emojiRecyclerView, 4)
|
||||
|
||||
// Test Edit mode
|
||||
|
@ -283,6 +285,7 @@ class UiAllScreensSanityTest {
|
|||
clickListItem(R.id.matrixProfileRecyclerView, 9)
|
||||
// File tab
|
||||
clickOn(R.string.uploads_files_title)
|
||||
sleep(1000)
|
||||
pressBack()
|
||||
|
||||
assertDisplayed(R.id.roomProfileAvatarView)
|
||||
|
@ -334,6 +337,7 @@ class UiAllScreensSanityTest {
|
|||
private fun navigateToRoomPeople() {
|
||||
// Open first user
|
||||
clickListItem(R.id.roomSettingsRecyclerView, 1)
|
||||
sleep(1000)
|
||||
assertDisplayed(R.id.memberProfilePowerLevelView)
|
||||
|
||||
// Verification
|
||||
|
@ -342,8 +346,9 @@ class UiAllScreensSanityTest {
|
|||
|
||||
// Role
|
||||
clickListItem(R.id.matrixProfileRecyclerView, 3)
|
||||
sleep(1000)
|
||||
clickDialogNegativeButton()
|
||||
|
||||
sleep(1000)
|
||||
clickBack()
|
||||
}
|
||||
|
||||
|
|
|
@ -330,6 +330,7 @@
|
|||
<activity android:name=".features.spaces.SpaceCreationActivity" />
|
||||
<activity android:name=".features.spaces.manage.SpaceManageActivity" />
|
||||
<activity android:name=".features.spaces.people.SpacePeopleActivity" />
|
||||
<activity android:name=".features.spaces.leave.SpaceLeaveAdvancedActivity" />
|
||||
<!-- Services -->
|
||||
|
||||
<service
|
||||
|
|
|
@ -61,7 +61,20 @@ class AppStateHandler @Inject constructor(
|
|||
|
||||
var onSwitchSpaceListener: OnSwitchSpaceListener? = null
|
||||
|
||||
fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? = selectedSpaceDataSource.currentValue?.orNull()
|
||||
fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? {
|
||||
// XXX we should somehow make it live :/ just a work around
|
||||
// For example just after creating a space and switching to it the
|
||||
// name in the app Bar could show Empty Room, and it will not update unless you
|
||||
// switch space
|
||||
return selectedSpaceDataSource.currentValue?.orNull()?.let {
|
||||
if (it is RoomGroupingMethod.BySpace) {
|
||||
// try to refresh sum?
|
||||
it.spaceSummary?.roomId?.let { activeSessionHolder.getSafeActiveSession()?.getRoomSummary(it) }?.let {
|
||||
RoomGroupingMethod.BySpace(it)
|
||||
} ?: it
|
||||
} else it
|
||||
}
|
||||
}
|
||||
|
||||
fun setCurrentSpace(spaceId: String?, session: Session? = null) {
|
||||
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
||||
|
|
|
@ -143,9 +143,11 @@ import im.vector.app.features.signout.soft.SoftLogoutFragment
|
|||
import im.vector.app.features.spaces.SpaceListFragment
|
||||
import im.vector.app.features.spaces.create.ChoosePrivateSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.ChooseSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceAdd3pidInvitesFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
||||
import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedFragment
|
||||
import im.vector.app.features.spaces.manage.SpaceAddRoomFragment
|
||||
import im.vector.app.features.spaces.manage.SpaceManageRoomsFragment
|
||||
import im.vector.app.features.spaces.manage.SpaceSettingsFragment
|
||||
|
@ -799,6 +801,11 @@ interface FragmentModule {
|
|||
@FragmentKey(ChoosePrivateSpaceTypeFragment::class)
|
||||
fun bindChoosePrivateSpaceTypeFragment(fragment: ChoosePrivateSpaceTypeFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(CreateSpaceAdd3pidInvitesFragment::class)
|
||||
fun bindCreateSpaceAdd3pidInvitesFragment(fragment: CreateSpaceAdd3pidInvitesFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SpaceAddRoomFragment::class)
|
||||
|
@ -828,4 +835,9 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(RoomJoinRuleChooseRestrictedFragment::class)
|
||||
fun bindRoomJoinRuleChooseRestrictedFragment(fragment: RoomJoinRuleChooseRestrictedFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SpaceLeaveAdvancedFragment::class)
|
||||
fun bindSpaceLeaveAdvancedFragment(fragment: SpaceLeaveAdvancedFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -84,10 +84,12 @@ import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet
|
|||
import im.vector.app.features.share.IncomingShareActivity
|
||||
import im.vector.app.features.signout.soft.SoftLogoutActivity
|
||||
import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet
|
||||
import im.vector.app.features.spaces.LeaveSpaceBottomSheet
|
||||
import im.vector.app.features.spaces.SpaceCreationActivity
|
||||
import im.vector.app.features.spaces.SpaceExploreActivity
|
||||
import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
|
||||
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
|
||||
import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity
|
||||
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
||||
import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
|
||||
import im.vector.app.features.terms.ReviewTermsActivity
|
||||
|
@ -96,6 +98,7 @@ import im.vector.app.features.usercode.UserCodeActivity
|
|||
import im.vector.app.features.widgets.WidgetActivity
|
||||
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
|
||||
import im.vector.app.features.workers.signout.SignOutBottomSheetDialogFragment
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
|
@ -127,6 +130,7 @@ interface ScreenComponent {
|
|||
fun uiStateRepository(): UiStateRepository
|
||||
fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
|
||||
fun autoAcceptInvites(): AutoAcceptInvites
|
||||
fun appCoroutineScope(): CoroutineScope
|
||||
|
||||
/* ==========================================================================================
|
||||
* Activities
|
||||
|
@ -171,6 +175,7 @@ interface ScreenComponent {
|
|||
fun inject(activity: SpaceExploreActivity)
|
||||
fun inject(activity: SpaceManageActivity)
|
||||
fun inject(activity: RoomJoinRuleActivity)
|
||||
fun inject(activity: SpaceLeaveAdvancedActivity)
|
||||
|
||||
/* ==========================================================================================
|
||||
* BottomSheets
|
||||
|
@ -199,6 +204,7 @@ interface ScreenComponent {
|
|||
fun inject(bottomSheet: SpaceInviteBottomSheet)
|
||||
fun inject(bottomSheet: JoinReplacementRoomBottomSheet)
|
||||
fun inject(bottomSheet: MigrateRoomBottomSheet)
|
||||
fun inject(bottomSheet: LeaveSpaceBottomSheet)
|
||||
|
||||
/* ==========================================================================================
|
||||
* Others
|
||||
|
|
|
@ -58,8 +58,10 @@ import im.vector.app.features.rageshake.VectorFileLogger
|
|||
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.app.features.reactions.data.EmojiDataSource
|
||||
import im.vector.app.features.session.SessionListener
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
||||
|
@ -145,6 +147,8 @@ interface VectorComponent {
|
|||
|
||||
fun vectorPreferences(): VectorPreferences
|
||||
|
||||
fun vectorDataStore(): VectorDataStore
|
||||
|
||||
fun wifiDetector(): WifiDetector
|
||||
|
||||
fun vectorFileLogger(): VectorFileLogger
|
||||
|
@ -165,6 +169,8 @@ interface VectorComponent {
|
|||
|
||||
fun webRtcCallManager(): WebRtcCallManager
|
||||
|
||||
fun appCoroutineScope(): CoroutineScope
|
||||
|
||||
fun jitsiActiveConferenceHolder(): JitsiActiveConferenceHolder
|
||||
|
||||
@Component.Factory
|
||||
|
|
|
@ -33,12 +33,16 @@ import im.vector.app.features.pin.PinCodeStore
|
|||
import im.vector.app.features.pin.SharedPrefPinCodeStore
|
||||
import im.vector.app.features.ui.SharedPreferencesUiStateRepository
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
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.raw.RawService
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
abstract class VectorModule {
|
||||
|
@ -94,6 +98,13 @@ abstract class VectorModule {
|
|||
fun providesHomeServerHistoryService(matrix: Matrix): HomeServerHistoryService {
|
||||
return matrix.homeServerHistoryService()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
@Singleton
|
||||
fun providesApplicationCoroutineScope(): CoroutineScope {
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
|
|
|
@ -39,13 +39,15 @@ import im.vector.app.features.themes.ThemeUtils
|
|||
/**
|
||||
* Set a text in the TextView, or set visibility to GONE if the text is null
|
||||
*/
|
||||
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true) {
|
||||
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true, vararg relatedViews: View = emptyArray()) {
|
||||
if (newText == null
|
||||
|| (newText.isBlank() && hideWhenBlank)) {
|
||||
isVisible = false
|
||||
relatedViews.forEach { it.isVisible = false }
|
||||
} else {
|
||||
this.text = newText
|
||||
isVisible = true
|
||||
relatedViews.forEach { it.isVisible = true }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
|
||||
package im.vector.app.core.extensions
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.isVisible
|
||||
import im.vector.app.R
|
||||
|
||||
/**
|
||||
|
@ -50,3 +53,8 @@ fun View.getMeasurements(): Pair<Int, Int> {
|
|||
val height = measuredHeight
|
||||
return width to height
|
||||
}
|
||||
|
||||
fun ImageView.setDrawableOrHide(drawableRes: Drawable?) {
|
||||
setImageDrawable(drawableRes)
|
||||
isVisible = drawableRes != null
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.core.platform
|
||||
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
|
||||
fun <T> LifecycleOwner.lifecycleAwareLazy(initializer: () -> T): Lazy<T> = LifecycleAwareLazy(this, initializer)
|
||||
|
||||
private object UninitializedValue
|
||||
|
||||
class LifecycleAwareLazy<out T>(
|
||||
private val owner: LifecycleOwner,
|
||||
initializer: () -> T
|
||||
) : Lazy<T>, LifecycleObserver {
|
||||
|
||||
private var initializer: (() -> T)? = initializer
|
||||
|
||||
private var _value: Any? = UninitializedValue
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override val value: T
|
||||
@MainThread
|
||||
get() {
|
||||
if (_value === UninitializedValue) {
|
||||
_value = initializer!!()
|
||||
attachToLifecycle()
|
||||
}
|
||||
return _value as T
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
fun resetValue() {
|
||||
_value = UninitializedValue
|
||||
detachFromLifecycle()
|
||||
}
|
||||
|
||||
private fun attachToLifecycle() {
|
||||
if (getLifecycleOwner().lifecycle.currentState == Lifecycle.State.DESTROYED) {
|
||||
throw IllegalStateException("Initialization failed because lifecycle has been destroyed!")
|
||||
}
|
||||
getLifecycleOwner().lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
private fun detachFromLifecycle() {
|
||||
getLifecycleOwner().lifecycle.removeObserver(this)
|
||||
}
|
||||
|
||||
private fun getLifecycleOwner() = when (owner) {
|
||||
is Fragment -> owner.viewLifecycleOwner
|
||||
else -> owner
|
||||
}
|
||||
|
||||
override fun isInitialized(): Boolean = _value !== UninitializedValue
|
||||
|
||||
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
|
||||
}
|
|
@ -62,6 +62,23 @@ class PushersManager @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun registerEmailForPush(email: String) {
|
||||
val currentSession = activeSessionHolder.getActiveSession()
|
||||
val appName = appNameProvider.getAppName()
|
||||
currentSession.addEmailPusher(
|
||||
email = email,
|
||||
lang = localeProvider.current().language,
|
||||
emailBranding = appName,
|
||||
appDisplayName = appName,
|
||||
deviceDisplayName = currentSession.sessionParams.deviceId ?: "MOBILE"
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun unregisterEmailPusher(email: String) {
|
||||
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
currentSession.removeEmailPusher(email)
|
||||
}
|
||||
|
||||
suspend fun unregisterPusher(context: Context, pushKey: String) {
|
||||
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
currentSession.removeHttpPusher(pushKey, getPusherAppId(context))
|
||||
|
|
|
@ -40,12 +40,14 @@ import im.vector.app.features.notifications.NotifiableEventResolver
|
|||
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.settings.BackgroundSyncMode
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.unifiedpush.android.connector.MessagingReceiver
|
||||
import org.unifiedpush.android.connector.MessagingReceiverHandler
|
||||
|
@ -69,6 +71,8 @@ data class Counts(
|
|||
val unread: Int = 0
|
||||
)
|
||||
|
||||
private val loggerTag = LoggerTag("Push", LoggerTag.SYNC)
|
||||
|
||||
/**
|
||||
* UnifiedPush handler.
|
||||
*/
|
||||
|
@ -79,6 +83,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
private lateinit var pusherManager: PushersManager
|
||||
private lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
private lateinit var vectorPreferences: VectorPreferences
|
||||
private lateinit var vectorDataStore: VectorDataStore
|
||||
private lateinit var wifiDetector: WifiDetector
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||
|
@ -95,6 +100,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
pusherManager = pusherManager()
|
||||
activeSessionHolder = activeSessionHolder()
|
||||
vectorPreferences = vectorPreferences()
|
||||
vectorDataStore = vectorDataStore()
|
||||
wifiDetector = wifiDetector()
|
||||
}
|
||||
}
|
||||
|
@ -108,9 +114,14 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
override fun onMessage(context: Context?, message: String, instance: String) {
|
||||
initVar(context!!)
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.d("## onMessageReceived() %s", message)
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceived() %s", message)
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceived()")
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
vectorDataStore.incrementPushCounter()
|
||||
}
|
||||
Timber.d("## onMessage() received")
|
||||
|
||||
val moshi: Moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
|
@ -127,6 +138,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
notification.unread = notification.counts.unread
|
||||
}
|
||||
|
||||
|
||||
// Diagnostic Push
|
||||
if (notification.eventId == PushersManager.TEST_EVENT_ID) {
|
||||
val intent = Intent(NotificationUtils.PUSH_ACTION)
|
||||
|
@ -135,14 +147,14 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
}
|
||||
|
||||
if (!vectorPreferences.areNotificationEnabledForDevice()) {
|
||||
Timber.i("Notification are disabled for this device")
|
||||
Timber.tag(loggerTag.value).i("Notification are disabled for this device")
|
||||
return
|
||||
}
|
||||
|
||||
mUIHandler.post {
|
||||
if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
// we are in foreground, let the sync do the things?
|
||||
Timber.d("PUSH received in a foreground state, ignore")
|
||||
Timber.tag(loggerTag.value).d("PUSH received in a foreground state, ignore")
|
||||
} else {
|
||||
onMessageReceivedInternal(context, notification)
|
||||
}
|
||||
|
@ -157,7 +169,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
*/
|
||||
override fun onNewEndpoint(context: Context?, endpoint: String, instance: String) {
|
||||
initVar(context!!)
|
||||
Timber.i("onNewEndpoint: adding $endpoint")
|
||||
Timber.tag(loggerTag.value).i("onNewEndpoint: adding $endpoint")
|
||||
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
|
||||
val gateway = UPHelper.customOrDefaultGateway(context, endpoint)
|
||||
if (UPHelper.getUpEndpoint(context) != endpoint
|
||||
|
@ -166,7 +178,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
UPHelper.storeUpEndpoint(context, endpoint)
|
||||
pusherManager.registerPusher(context, endpoint, gateway)
|
||||
} else {
|
||||
Timber.i("onNewEndpoint: skipped")
|
||||
Timber.tag(loggerTag.value).i("onNewEndpoint: skipped")
|
||||
}
|
||||
}
|
||||
if (!UPHelper.allowBackgroundSync(context)) {
|
||||
|
@ -184,7 +196,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
}
|
||||
|
||||
override fun onUnregistered(context: Context?, instance: String) {
|
||||
Timber.d("Unifiedpush: Unregistered")
|
||||
Timber.tag(loggerTag.value).d("Unifiedpush: Unregistered")
|
||||
initVar(context!!)
|
||||
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY
|
||||
vectorPreferences.setFdroidSyncBackgroundMode(mode)
|
||||
|
@ -192,7 +204,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
try {
|
||||
pusherManager.unregisterPusher(context, UPHelper.getUpEndpoint(context)!!)
|
||||
} catch (e: Exception) {
|
||||
Timber.d("Probably unregistering a non existant pusher")
|
||||
Timber.tag(loggerTag.value).d("Probably unregistering a non existant pusher")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +217,9 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
private fun onMessageReceivedInternal(context: Context, notification: Notification) {
|
||||
try {
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.d("## onMessageReceivedInternal() : $notification")
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal() : $notification")
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal()")
|
||||
}
|
||||
|
||||
// update the badge counter
|
||||
|
@ -214,21 +228,21 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
val session = activeSessionHolder.getSafeActiveSession()
|
||||
|
||||
if (session == null) {
|
||||
Timber.w("## Can't sync from push, no current session")
|
||||
Timber.tag(loggerTag.value).w("## Can't sync from push, no current session")
|
||||
} else {
|
||||
if (isEventAlreadyKnown(notification.eventId, notification.roomId)) {
|
||||
Timber.d("Ignoring push, event already known")
|
||||
Timber.tag(loggerTag.value).d("Ignoring push, event already known")
|
||||
} else {
|
||||
// Try to get the Event content faster
|
||||
Timber.d("Requesting event in fast lane")
|
||||
Timber.tag(loggerTag.value).d("Requesting event in fast lane")
|
||||
getEventFastLane(session, notification.roomId, notification.eventId)
|
||||
|
||||
Timber.d("Requesting background sync")
|
||||
Timber.tag(loggerTag.value).d("Requesting background sync")
|
||||
session.requireBackgroundSync()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## onMessageReceivedInternal() failed")
|
||||
Timber.tag(loggerTag.value).e(e, "## onMessageReceivedInternal() failed")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,18 +256,18 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
}
|
||||
|
||||
if (wifiDetector.isConnectedToWifi().not()) {
|
||||
Timber.d("No WiFi network, do not get Event")
|
||||
Timber.tag(loggerTag.value).d("No WiFi network, do not get Event")
|
||||
return
|
||||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
Timber.d("Fast lane: start request")
|
||||
Timber.tag(loggerTag.value).d("Fast lane: start request")
|
||||
val event = tryOrNull { session.getEvent(roomId, eventId) } ?: return@launch
|
||||
|
||||
val resolvedEvent = notifiableEventResolver.resolveInMemoryEvent(session, event)
|
||||
|
||||
resolvedEvent
|
||||
?.also { Timber.d("Fast lane: notify drawer") }
|
||||
?.also { Timber.tag(loggerTag.value).d("Fast lane: notify drawer") }
|
||||
?.let {
|
||||
it.isPushGatewayEvent = true
|
||||
notificationDrawerManager.onNotifiableEventReceived(it)
|
||||
|
@ -271,7 +285,7 @@ val upHandler = object: MessagingReceiverHandler {
|
|||
val room = session.getRoom(roomId) ?: return false
|
||||
return room.getTimeLineEvent(eventId) != null
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## isEventAlreadyKnown() : failed to check if the event was already defined")
|
||||
Timber.tag(loggerTag.value).e(e, "## isEventAlreadyKnown() : failed to check if the event was already defined")
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -50,7 +50,7 @@ private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP)
|
|||
class CallService : VectorService() {
|
||||
|
||||
private val connections = mutableMapOf<String, CallConnection>()
|
||||
private val knownCalls = mutableSetOf<CallInformation>()
|
||||
private val knownCalls = mutableMapOf<String, CallInformation>()
|
||||
private val connectedCallIds = mutableSetOf<String>()
|
||||
|
||||
private lateinit var notificationManager: NotificationManagerCompat
|
||||
|
@ -190,7 +190,7 @@ class CallService : VectorService() {
|
|||
} else {
|
||||
notificationManager.notify(callId.hashCode(), notification)
|
||||
}
|
||||
knownCalls.add(callInformation)
|
||||
knownCalls[callId] = callInformation
|
||||
}
|
||||
|
||||
private fun handleCallTerminated(intent: Intent) {
|
||||
|
@ -198,20 +198,22 @@ class CallService : VectorService() {
|
|||
val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason
|
||||
val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
|
||||
alertManager.cancelAlert(callId)
|
||||
val terminatedCall = knownCalls.firstOrNull { it.callId == callId }
|
||||
val terminatedCall = knownCalls.remove(callId)
|
||||
if (terminatedCall == null) {
|
||||
Timber.tag(loggerTag.value).v("Call terminated for unknown call $callId$")
|
||||
Timber.tag(loggerTag.value).v("Call terminated for unknown call $callId")
|
||||
handleUnexpectedState(callId)
|
||||
return
|
||||
}
|
||||
knownCalls.remove(terminatedCall)
|
||||
val notification = notificationUtils.buildCallEndedNotification(false)
|
||||
val notificationId = callId.hashCode()
|
||||
startForeground(notificationId, notification)
|
||||
if (knownCalls.isEmpty()) {
|
||||
Timber.tag(loggerTag.value).v("No more call, stop the service")
|
||||
stopForeground(true)
|
||||
mediaSession?.isActive = false
|
||||
myStopSelf()
|
||||
}
|
||||
val wasConnected = connectedCallIds.remove(callId)
|
||||
val notification = notificationUtils.buildCallEndedNotification(terminatedCall.isVideoCall)
|
||||
notificationManager.notify(callId.hashCode(), notification)
|
||||
if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) {
|
||||
val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
||||
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification)
|
||||
|
@ -243,7 +245,7 @@ class CallService : VectorService() {
|
|||
} else {
|
||||
notificationManager.notify(callId.hashCode(), notification)
|
||||
}
|
||||
knownCalls.add(callInformation)
|
||||
knownCalls[callId] = callInformation
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,18 +269,19 @@ class CallService : VectorService() {
|
|||
} else {
|
||||
notificationManager.notify(callId.hashCode(), notification)
|
||||
}
|
||||
knownCalls.add(callInformation)
|
||||
knownCalls[callId] = callInformation
|
||||
}
|
||||
|
||||
private fun handleUnexpectedState(callId: String?) {
|
||||
Timber.tag(loggerTag.value).v("Fallback to clear everything")
|
||||
callRingPlayerIncoming?.stop()
|
||||
callRingPlayerOutgoing?.stop()
|
||||
if (callId != null) {
|
||||
notificationManager.cancel(callId.hashCode())
|
||||
}
|
||||
val notification = notificationUtils.buildCallEndedNotification(false)
|
||||
startForeground(DEFAULT_NOTIFICATION_ID, notification)
|
||||
if (callId != null) {
|
||||
startForeground(callId.hashCode(), notification)
|
||||
} else {
|
||||
startForeground(DEFAULT_NOTIFICATION_ID, notification)
|
||||
}
|
||||
if (knownCalls.isEmpty()) {
|
||||
mediaSession?.isActive = false
|
||||
myStopSelf()
|
||||
|
@ -371,7 +374,7 @@ class CallService : VectorService() {
|
|||
putExtra(EXTRA_END_CALL_REASON, endCallReason)
|
||||
putExtra(EXTRA_END_CALL_REJECTED, rejected)
|
||||
}
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.core.view.isGone
|
|||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.setDrawableOrHide
|
||||
import im.vector.app.core.extensions.setTextOrHide
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.databinding.ViewBottomSheetActionButtonBinding
|
||||
|
@ -81,7 +82,7 @@ class BottomSheetActionButton @JvmOverloads constructor(
|
|||
var rightIcon: Drawable? = null
|
||||
set(value) {
|
||||
field = value
|
||||
views.bottomSheetActionIcon.setImageDrawable(value)
|
||||
views.bottomSheetActionIcon.setDrawableOrHide(value)
|
||||
}
|
||||
|
||||
var tint: Int? = null
|
||||
|
@ -96,6 +97,12 @@ class BottomSheetActionButton @JvmOverloads constructor(
|
|||
value?.let { views.bottomSheetActionTitle.setTextColor(it) }
|
||||
}
|
||||
|
||||
var isBetaAction: Boolean? = null
|
||||
set(value) {
|
||||
field = value
|
||||
views.bottomSheetActionBeta.isVisible = field ?: false
|
||||
}
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_bottom_sheet_action_button, this)
|
||||
views = ViewBottomSheetActionButtonBinding.bind(this)
|
||||
|
@ -110,6 +117,8 @@ class BottomSheetActionButton @JvmOverloads constructor(
|
|||
|
||||
tint = getColor(R.styleable.BottomSheetActionButton_tint, ThemeUtils.getColor(context, android.R.attr.textColor))
|
||||
titleTextColor = getColor(R.styleable.BottomSheetActionButton_titleTextColor, ThemeUtils.getColor(context, R.attr.colorPrimary))
|
||||
|
||||
isBetaAction = getBoolean(R.styleable.BottomSheetActionButton_betaAction, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class CurrentCallsViewPresenter {
|
|||
this.currentCall = currentCall
|
||||
this.currentCall?.addListener(tickListener)
|
||||
this.calls = calls
|
||||
val hasActiveCall = currentCall != null
|
||||
val hasActiveCall = calls.isNotEmpty()
|
||||
currentCallsView?.isVisible = hasActiveCall
|
||||
currentCallsView?.render(calls, currentCall?.formattedDuration() ?: "")
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package im.vector.app.core.ui.views
|
|||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import im.vector.app.R
|
||||
import im.vector.app.databinding.ViewFailedMessagesWarningBinding
|
||||
|
||||
|
@ -49,8 +48,4 @@ class FailedMessagesWarningView @JvmOverloads constructor(
|
|||
views.failedMessagesDeleteAllButton.setOnClickListener { callback?.onDeleteAllClicked() }
|
||||
views.failedMessagesRetryButton.setOnClickListener { callback?.onRetryClicked() }
|
||||
}
|
||||
|
||||
fun render(hasFailedMessages: Boolean) {
|
||||
isVisible = hasFailedMessages
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context
|
|||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import im.vector.app.R
|
||||
|
||||
/**
|
||||
* Open a web view above the current activity.
|
||||
|
@ -38,3 +39,14 @@ fun Context.displayInWebView(url: String) {
|
|||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, consentCallBack: (() -> Unit)) {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.identity_server_consent_dialog_title)
|
||||
.setMessage(getString(R.string.identity_server_consent_dialog_content, configuredIdentityServer ?: ""))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
consentCallBack.invoke()
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import im.vector.app.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.app.features.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.app.features.reactions.data.EmojiDataSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteEmojiPresenter @Inject constructor(context: Context,
|
||||
|
@ -28,11 +33,14 @@ class AutocompleteEmojiPresenter @Inject constructor(context: Context,
|
|||
private val controller: AutocompleteEmojiController) :
|
||||
RecyclerViewPresenter<String>(context), AutocompleteClickListener<String> {
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
|
||||
init {
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
coroutineScope.coroutineContext.cancelChildren()
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
|
@ -45,12 +53,14 @@ class AutocompleteEmojiPresenter @Inject constructor(context: Context,
|
|||
}
|
||||
|
||||
override fun onQuery(query: CharSequence?) {
|
||||
val data = if (query.isNullOrBlank()) {
|
||||
// Return common emojis
|
||||
emojiDataSource.getQuickReactions()
|
||||
} else {
|
||||
emojiDataSource.filterWith(query.toString())
|
||||
coroutineScope.launch {
|
||||
val data = if (query.isNullOrBlank()) {
|
||||
// Return common emojis
|
||||
emojiDataSource.getQuickReactions()
|
||||
} else {
|
||||
emojiDataSource.filterWith(query.toString())
|
||||
}
|
||||
controller.setData(data)
|
||||
}
|
||||
controller.setData(data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ package im.vector.app.features.autocomplete.member
|
|||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.app.features.autocomplete.RecyclerViewPresenter
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
|
@ -35,7 +35,7 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
|
|||
private val controller: AutocompleteMemberController
|
||||
) : RecyclerViewPresenter<RoomMemberSummary>(context), AutocompleteClickListener<RoomMemberSummary> {
|
||||
|
||||
private val room = session.getRoom(roomId)!!
|
||||
private val room by lazy { session.getRoom(roomId)!! }
|
||||
|
||||
init {
|
||||
controller.listener = this
|
||||
|
|
|
@ -41,7 +41,7 @@ class SharedKnownCallsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private val currentCallListener = object : WebRtcCallManager.CurrentCallListener {
|
||||
private val callManagerListener = object : WebRtcCallManager.Listener {
|
||||
override fun onCurrentCallChange(call: WebRtcCall?) {
|
||||
val knownCalls = callManager.getCalls()
|
||||
liveKnownCalls.postValue(knownCalls)
|
||||
|
@ -50,12 +50,17 @@ class SharedKnownCallsViewModel @Inject constructor(
|
|||
it.addListener(callListener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCallEnded(callId: String) {
|
||||
val knownCalls = callManager.getCalls()
|
||||
liveKnownCalls.postValue(knownCalls)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val knownCalls = callManager.getCalls()
|
||||
liveKnownCalls.postValue(knownCalls)
|
||||
callManager.addCurrentCallListener(currentCallListener)
|
||||
callManager.addListener(callManagerListener)
|
||||
knownCalls.forEach {
|
||||
it.addListener(callListener)
|
||||
}
|
||||
|
@ -65,7 +70,7 @@ class SharedKnownCallsViewModel @Inject constructor(
|
|||
callManager.getCalls().forEach {
|
||||
it.removeListener(callListener)
|
||||
}
|
||||
callManager.removeCurrentCallListener(currentCallListener)
|
||||
callManager.removeListener(callManagerListener)
|
||||
super.onCleared()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,7 +134,15 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
} ?: VectorCallViewState.TransfereeState.UnknownTransferee
|
||||
}
|
||||
|
||||
private val currentCallListener = object : WebRtcCallManager.CurrentCallListener {
|
||||
private val callManagerListener = object : WebRtcCallManager.Listener {
|
||||
|
||||
override fun onCallEnded(callId: String) {
|
||||
withState { state ->
|
||||
if (state.otherKnownCallInfo?.callId == callId) {
|
||||
setState { copy(otherKnownCallInfo = null) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCurrentCallChange(call: WebRtcCall?) {
|
||||
if (call != null) {
|
||||
|
@ -159,9 +167,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun updateOtherKnownCall(currentCall: WebRtcCall) {
|
||||
val otherCall = callManager.getCalls().firstOrNull {
|
||||
it.callId != currentCall.callId && it.mxCall.state is CallState.Connected
|
||||
}
|
||||
val otherCall = getOtherKnownCall(currentCall)
|
||||
setState {
|
||||
if (otherCall == null) {
|
||||
copy(otherKnownCallInfo = null)
|
||||
|
@ -171,6 +177,12 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getOtherKnownCall(currentCall: WebRtcCall): WebRtcCall? {
|
||||
return callManager.getCalls().firstOrNull {
|
||||
it.callId != currentCall.callId && it.mxCall.state is CallState.Connected
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setupCallWithCurrentState()
|
||||
}
|
||||
|
@ -184,7 +196,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
}
|
||||
} else {
|
||||
call = webRtcCall
|
||||
callManager.addCurrentCallListener(currentCallListener)
|
||||
callManager.addListener(callManagerListener)
|
||||
webRtcCall.addListener(callListener)
|
||||
val currentSoundDevice = callManager.audioManager.selectedDevice
|
||||
if (currentSoundDevice == CallAudioManager.Device.Phone) {
|
||||
|
@ -230,7 +242,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onCleared() {
|
||||
callManager.removeCurrentCallListener(currentCallListener)
|
||||
callManager.removeListener(callManagerListener)
|
||||
call?.removeListener(callListener)
|
||||
call = null
|
||||
proximityManager.stop()
|
||||
|
@ -310,10 +322,10 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
VectorCallViewEvents.ShowCallTransferScreen
|
||||
)
|
||||
}
|
||||
VectorCallViewActions.TransferCall -> {
|
||||
VectorCallViewActions.TransferCall -> {
|
||||
handleCallTransfer()
|
||||
}
|
||||
is VectorCallViewActions.SwitchCall -> {
|
||||
is VectorCallViewActions.SwitchCall -> {
|
||||
setState { VectorCallViewState(action.callArgs) }
|
||||
setupCallWithCurrentState()
|
||||
}
|
||||
|
|
|
@ -28,20 +28,21 @@ import com.facebook.react.bridge.JavaOnlyMap
|
|||
import org.jitsi.meet.sdk.BroadcastEmitter
|
||||
import org.jitsi.meet.sdk.BroadcastEvent
|
||||
import org.jitsi.meet.sdk.JitsiMeet
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import timber.log.Timber
|
||||
|
||||
private const val CONFERENCE_URL_DATA_KEY = "url"
|
||||
|
||||
fun BroadcastEvent.extractConferenceUrl(): String? {
|
||||
return when (type) {
|
||||
BroadcastEvent.Type.CONFERENCE_TERMINATED,
|
||||
BroadcastEvent.Type.CONFERENCE_WILL_JOIN,
|
||||
BroadcastEvent.Type.CONFERENCE_JOINED -> data[CONFERENCE_URL_DATA_KEY] as? String
|
||||
else -> null
|
||||
sealed class ConferenceEvent(open val data: Map<String, Any>) {
|
||||
data class Terminated(override val data: Map<String, Any>) : ConferenceEvent(data)
|
||||
data class WillJoin(override val data: Map<String, Any>) : ConferenceEvent(data)
|
||||
data class Joined(override val data: Map<String, Any>) : ConferenceEvent(data)
|
||||
|
||||
fun extractConferenceUrl(): String? {
|
||||
return data[CONFERENCE_URL_DATA_KEY] as? String
|
||||
}
|
||||
}
|
||||
|
||||
class JitsiBroadcastEmitter(private val context: Context) {
|
||||
class ConferenceEventEmitter(private val context: Context) {
|
||||
|
||||
fun emitConferenceEnded() {
|
||||
val broadcastEventData = JavaOnlyMap.of(CONFERENCE_URL_DATA_KEY, JitsiMeet.getCurrentConference())
|
||||
|
@ -49,8 +50,9 @@ class JitsiBroadcastEmitter(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
class JitsiBroadcastEventObserver(private val context: Context,
|
||||
private val onBroadcastEvent: (BroadcastEvent) -> Unit) : LifecycleObserver {
|
||||
class ConferenceEventObserver(private val context: Context,
|
||||
private val onBroadcastEvent: (ConferenceEvent) -> Unit)
|
||||
: LifecycleObserver {
|
||||
|
||||
// See https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk#listening-for-broadcasted-events
|
||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||
|
@ -61,8 +63,10 @@ class JitsiBroadcastEventObserver(private val context: Context,
|
|||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
fun unregisterForBroadcastMessages() {
|
||||
tryOrNull("Unable to unregister receiver") {
|
||||
try {
|
||||
LocalBroadcastManager.getInstance(context).unregisterReceiver(broadcastReceiver)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.v("Unable to unregister receiver")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,13 +76,23 @@ class JitsiBroadcastEventObserver(private val context: Context,
|
|||
for (type in BroadcastEvent.Type.values()) {
|
||||
intentFilter.addAction(type.action)
|
||||
}
|
||||
tryOrNull("Unable to register receiver") {
|
||||
try {
|
||||
LocalBroadcastManager.getInstance(context).registerReceiver(broadcastReceiver, intentFilter)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.v("Unable to register receiver")
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBroadcastReceived(intent: Intent) {
|
||||
val event = BroadcastEvent(intent)
|
||||
onBroadcastEvent(event)
|
||||
val conferenceEvent = when (event.type) {
|
||||
BroadcastEvent.Type.CONFERENCE_JOINED -> ConferenceEvent.Joined(event.data)
|
||||
BroadcastEvent.Type.CONFERENCE_TERMINATED -> ConferenceEvent.Terminated(event.data)
|
||||
BroadcastEvent.Type.CONFERENCE_WILL_JOIN -> ConferenceEvent.WillJoin(event.data)
|
||||
else -> null
|
||||
}
|
||||
if (conferenceEvent != null) {
|
||||
onBroadcastEvent(conferenceEvent)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue